Tại sao tôi không thể sử dụng các biến trong T-SQL như tôi tưởng tượng?


18

Xin lỗi, tôi là một nhà phát triển đã chuyển sang thế giới của SQL. Tôi nghĩ rằng tôi có thể cải thiện một số SQL bằng cách thêm các biến nhưng nó không hoạt động như tôi mong đợi. Ai đó có thể cho tôi biết tại sao điều này không hoạt động? Tôi không muốn làm việc xung quanh, tôi muốn biết lý do tại sao điều này không hoạt động như tôi tưởng tượng vì tôi chắc chắn có một lý do chính đáng, nhưng hiện tại nó không nhảy ra khỏi tôi.

DECLARE @DatabaseName varchar(150)
SET @DatabaseName = 'MyAmazingDatabaseName'

CREATE DATABASE @DatabaseName
GO

USE @DatabaseName
GO

Bạn cần sử dụng sql động cho điều đó. mssqltips.com/sqlservertip/1160/...
Stijn Wynants

3
Xin cảm ơn vì đã bình luận nhưng theo câu hỏi của tôi, đây không phải là câu trả lời tôi đang tìm kiếm. Tôi muốn biết nếu có ai biết tại sao tôi không thể làm điều đó như tôi đã thể hiện.
gareth

2
Bởi vì không thể sử dụng USElệnh với một tham số.
TT.

2
Ngoài các câu trả lời đã được đưa ra, nhưng không đủ cho câu trả lời của riêng nó, các biến được đặt trong phạm vi tối đa của lô hiện tại. (Tôi không biết nếu có thể phạm vi các biến hẹp hơn so với SQL Server.) Vì vậy, thời điểm bạn có GO, biến được khai báo trước đó sẽ biến mất. Bạn có thể muốn xem xét các biến SQLCMD, có thể hoặc không thể áp dụng cho kịch bản của bạn.
một CVn

1
Chúng ta có xu hướng nghĩ tên cơ sở dữ liệu và tên cột là giá trị chuỗi, nhưng trong ngữ cảnh của SQL, chúng là Mã định danh. Những gì bạn đang cố gắng sẽ giống như mong đợi x = 7; giống như 'x' = 7; trong một số ngôn ngữ khác. Giống như ngôn ngữ máy tính có thể được tạo ra có liên quan đến 'x' = 7 giống như x = 7, RDBMS có thể được tạo để xử lý Tạo Bảng X giống như Tạo Bảng 'X'. Nhưng đó không phải là SQL.
user1008646

Câu trả lời:


20

Trên trang Sách trực tuyến cho các biến

Các biến chỉ có thể được sử dụng trong các biểu thức, không thay cho tên đối tượng hoặc từ khóa. Để xây dựng các câu lệnh SQL động, hãy sử dụng EXECUTE.

Nó sẽ hoạt động theo cách bạn mong đợi, ví dụ, nếu bạn sử dụng biến của mình trong mệnh đề where. Về lý do tại sao, tôi sẽ nghĩ rằng nó có liên quan đến trình phân tích cú pháp không thể đánh giá biến và do đó kiểm tra sự tồn tại. Khi thực hiện, truy vấn được phân tích cú pháp trước cho cú pháp và đối tượng và sau đó, nếu phân tích cú pháp thành công, truy vấn sẽ thực thi tại điểm mà biến sẽ được đặt.

DECLARE @name varchar(20);
SET @name = 'test';

CREATE TABLE [#tmp]([val] varchar(10));

insert into #tmp
values('test')

SELECT *
FROM [#tmp]
WHERE [val] = @name;

3
Lưu ý rằng SQL động nên được tránh bất cứ khi nào có thể. Đó là SQL tương tự để sử dụng các evalhàm trong các ngôn ngữ thủ tục như JavaScript và Python. Đó là một cách nhanh chóng để tạo ra lỗ hổng bảo mật.
jpmc26

1
@ jpmc26: Cách an toàn hơn để làm điều đó không liên quan đến SQL động là gì?
Robert Harvey

1
@RobertHarvey Chỉ vì tốt nhất nên tránh không có nghĩa là luôn có một sự thay thế với chính xác cùng chức năng. ;) Thông thường, một phần của câu trả lời là "Sử dụng một giải pháp hoàn toàn khác cho vấn đề." Đôi khi đó là điều tốt nhất để làm, nhưng không phải không có sự cân nhắc kỹ lưỡng và đảm bảo rằng bạn đã không bỏ qua các lựa chọn thay thế, và thậm chí sau đó, nó nên đi kèm với một liều thuốc thận trọng.
jpmc26

2
@ jpmc26: Ví dụ của OP trông giống như ORM "Code-First" có thể làm gì để thiết lập các bảng trong cơ sở dữ liệu. Mặc dù về nguyên tắc, SQL động không an toàn, người dùng cuối sẽ không bao giờ chạm vào mã cụ thể đó.
Robert Harvey

@RobertHarvey Phụ thuộc vào người bạn coi là "người dùng cuối". Đối với tập lệnh triển khai DB, tôi coi nhà phát triển và có thể một số quản trị viên hệ thống là "người dùng cuối". Tôi vẫn sẽ thiết kế hệ thống để từ chối đầu vào không an toàn trong trường hợp đó, nếu không vì lý do nào khác ngoài việc tránh tai nạn. Ngoài ra, đối với "không bao giờ chạm", OP đang chạm vào mã này, vì vậy ...
jpmc26

17

Các hạn chế về việc sử dụng các biến trong các câu lệnh SQL phát sinh từ kiến ​​trúc của SQL.

Có ba giai đoạn trong quá trình xử lý câu lệnh SQL:

  1. Chuẩn bị - Câu lệnh được phân tích cú pháp và một kế hoạch thực hiện được biên dịch, chỉ định các đối tượng cơ sở dữ liệu nào được truy cập, cách chúng được truy cập và chúng có liên quan như thế nào. Kế hoạch thực hiện được lưu trong bộ đệm của kế hoạch .
  2. Ràng buộc - bất kỳ biến nào trong câu lệnh được thay thế bằng các giá trị thực tế.
  3. Thực thi - kế hoạch lưu trữ được thực hiện với các giá trị ràng buộc.

Máy chủ SQL ẩn bước chuẩn bị từ lập trình viên và thực thi nó nhanh hơn nhiều so với các cơ sở dữ liệu truyền thống như Oracle và DB2. Vì lý do hiệu năng mà SQL dành nhiều thời gian để xác định kế hoạch thực hiện tối ưu, nhưng chỉ thực hiện lần đầu tiên khi gặp câu lệnh sau khi khởi động lại.

Vì vậy, trong SQL tĩnh , các biến chỉ có thể được sử dụng ở những nơi chúng sẽ không làm mất hiệu lực kế hoạch thực hiện, vì vậy không phải cho tên bảng, tên cột (bao gồm cả tên cột trong điều kiện WHERE), v.v.

SQL động tồn tại cho các trường hợp mà người ta không thể làm việc với các hạn chế và lập trình viên biết rằng sẽ mất nhiều thời gian hơn để thực thi. SQL động có thể dễ bị tấn công bằng mã độc, vì vậy hãy cẩn thận!


7

Như bạn có thể thấy, câu hỏi "tại sao" đòi hỏi một loại câu trả lời khác, bao gồm cả lý do lịch sử và các giả định cơ bản cho ngôn ngữ, tôi không chắc mình thực sự có thể thực hiện công lý đó.

Bài viết toàn diện này của SQL MVP Erland Sommarskog không cố gắng cung cấp một số lý do, cùng với các cơ chế:

Lời nguyền và phước lành của SQL động :

Gói bộ nhớ truy vấn

Mỗi truy vấn bạn chạy trong SQL Server yêu cầu một kế hoạch truy vấn. Khi bạn chạy truy vấn lần đầu tiên, SQL Server sẽ xây dựng một kế hoạch truy vấn cho nó - hoặc theo thuật ngữ - nó sẽ biên dịch truy vấn. SQL Server lưu kế hoạch trong bộ đệm và lần sau khi bạn chạy truy vấn, kế hoạch được sử dụng lại.

Điều này (và bảo mật, xem bên dưới) có lẽ là lý do lớn nhất.

SQL hoạt động với tiền đề rằng các truy vấn không phải là hoạt động một lần, mà chúng sẽ được sử dụng nhiều lần. Nếu bảng (hoặc cơ sở dữ liệu!) Không thực sự được chỉ định trong truy vấn, thì không có cách nào để tạo và lưu kế hoạch thực hiện để sử dụng trong tương lai.

Có, không phải mọi truy vấn chúng tôi chạy sẽ được sử dụng lại, nhưng đây là tiền đề hoạt động mặc định của SQL , vì vậy "ngoại lệ" có nghĩa là đặc biệt.

Một vài lý do khác mà danh sách Erland (lưu ý rằng anh ta liệt kê rõ ràng lợi thế của việc sử dụng các thủ tục được lưu trữ , nhưng nhiều trong số đó cũng là lợi thế của các truy vấn được tham số hóa (không động)):

  • Hệ thống cấp phép : công cụ SQL không thể dự đoán liệu bạn có quyền chạy truy vấn hay không nếu nó không biết bảng (hoặc cơ sở dữ liệu) mà bạn sẽ hoạt động. "Chuỗi quyền" sử dụng SQL động là một nỗi đau ở mông.
  • Giảm lưu lượng mạng : Truyền tên của Proc được lưu trữ và một vài giá trị tham số qua mạng ngắn hơn một câu lệnh truy vấn dài.
  • Logic đóng gói : Bạn nên làm quen với những lợi thế của việc đóng gói logic từ các môi trường lập trình khác.
  • Theo dõi những gì được sử dụng : Nếu tôi cần thay đổi một định nghĩa cột, làm thế nào tôi có thể tìm thấy tất cả các mã gọi nó? Các thủ tục hệ thống tồn tại để tìm các phụ thuộc trong cơ sở dữ liệu SQL, nhưng chỉ khi mã nằm trong các thủ tục được lưu trữ.
  • Dễ viết mã SQL : Kiểm tra cú pháp xảy ra khi bạn tạo hoặc sửa đổi một thủ tục được lưu trữ, do đó hy vọng sẽ có ít lỗi hơn.
  • Giải quyết các lỗi và vấn đề : Một DBA có thể theo dõi và đo lường hiệu suất của các thủ tục được lưu trữ riêng lẻ dễ dàng hơn nhiều so với SQL động thay đổi liên tục.

Một lần nữa, mỗi trong số chúng có một trăm sắc thái mà tôi sẽ không nhận được ở đây.


2

Bạn cần sử dụng sql động

DECLARE @DatabaseName varchar(150) = 'dbamaint'
declare @sqltext nvarchar(max) = N''

set @sqltext = N'CREATE DATABASE '+quotename(@DatabaseName)+ ';'

print @sqltext 

-- once you are happy .. uncomment below
--exec sp_executesql @sqltext
set @sqltext = ''
set @sqltext = N'use '+quotename(@DatabaseName)+ ';'
print @sqltext 
-- once you are happy .. uncomment below
--exec sp_executesql @sqltext

bên dưới là đầu ra của bản in .. một khi bạn bỏ ghi chú, exec sp_executesql @sqltextcác câu lệnh sẽ được thực thi ...

CREATE DATABASE [dbamaint];
use [dbamaint];

1
Vâng cảm ơn, tôi biết điều đó, nhưng tôi muốn biết nếu có ai biết tại sao bạn không thể sử dụng biến?
gareth

Trình phân tích cú pháp T-SQL sẽ đưa ra các lỗi cú pháp. Đây không phải là một T-SQL hợp lệ mà trình phân tích cú pháp nhận ra.
Kin Shah

Cảm ơn Kin, tôi chắc chắn phải có lý do tốt cho nó. Có thể vì tên cơ sở dữ liệu có thể chứa '@' và có thể một số lý do phức tạp khác.
gareth

1
Vâng, chúng có thể chứa @ và tôi nghĩ đó là lý do chính. msdn.microsoft.com/en-us/l Library / ms175874.aspx
Paweł Tajs

1
@gazeranco Hãy tin tôi, bất kỳ ai làm việc với SQL Server chắc chắn đều mong muốn rằng nhiều lệnh sẽ chấp nhận các biến thay cho các định danh không đổi.
db2
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.