Có cách nào để làm cho biến TSQL không đổi?
Có cách nào để làm cho biến TSQL không đổi?
Câu trả lời:
Không, nhưng bạn có thể tạo một hàm và mã hóa nó vào đó và sử dụng nó.
Đây là một ví dụ:
CREATE FUNCTION fnConstant()
RETURNS INT
AS
BEGIN
RETURN 2
END
GO
SELECT dbo.fnConstant()
WITH SCHEMABINDING
trong CREATE FUNCTION
câu lệnh (trái ngược với trong một thủ tục được lưu trữ có thể đang gọi hàm) - có đúng không?
Một giải pháp do Jared Ko đưa ra là sử dụng hằng số giả .
Như đã giải thích trong SQL Server: Biến, Tham số hay Chữ viết? Hoặc… Hằng số? :
Pseudo-Constants không phải là biến hoặc tham số. Thay vào đó, chúng chỉ đơn giản là các chế độ xem có một hàng và đủ cột để hỗ trợ các hằng số của bạn. Với những quy tắc đơn giản này, SQL Engine hoàn toàn bỏ qua giá trị của khung nhìn nhưng vẫn xây dựng một kế hoạch thực thi dựa trên giá trị của nó. Kế hoạch thực hiện thậm chí không hiển thị một tham gia vào chế độ xem!
Tạo như thế này:
CREATE SCHEMA ShipMethod GO -- Each view can only have one row. -- Create one column for each desired constant. -- Each column is restricted to a single value. CREATE VIEW ShipMethod.ShipMethodID AS SELECT CAST(1 AS INT) AS [XRQ - TRUCK GROUND] ,CAST(2 AS INT) AS [ZY - EXPRESS] ,CAST(3 AS INT) AS [OVERSEAS - DELUXE] ,CAST(4 AS INT) AS [OVERNIGHT J-FAST] ,CAST(5 AS INT) AS [CARGO TRANSPORT 5]
Sau đó sử dụng như thế này:
SELECT h.* FROM Sales.SalesOrderHeader h JOIN ShipMethod.ShipMethodID const ON h.ShipMethodID = const.[OVERNIGHT J-FAST]
Hoặc như thế này:
SELECT h.* FROM Sales.SalesOrderHeader h WHERE h.ShipMethodID = (SELECT TOP 1 [OVERNIGHT J-FAST] FROM ShipMethod.ShipMethodID)
Cách giải quyết của tôi đối với các hằng số bị thiếu là đưa ra gợi ý về giá trị cho trình tối ưu hóa.
DECLARE @Constant INT = 123;
SELECT *
FROM [some_relation]
WHERE [some_attribute] = @Constant
OPTION( OPTIMIZE FOR (@Constant = 123))
Điều này yêu cầu trình biên dịch truy vấn xử lý biến như thể nó là một hằng số khi tạo kế hoạch thực thi. Mặt dưới là bạn phải xác định giá trị hai lần.
Không, nhưng nên sử dụng các quy ước đặt tên cũ tốt.
declare @MY_VALUE as int
FN_CONSTANT()
. Bằng cách đó, rõ ràng nó đang làm gì.
Không có hỗ trợ tích hợp cho các hằng số trong T-SQL. Bạn có thể sử dụng cách tiếp cận của SQLMenace để mô phỏng nó (mặc dù bạn không bao giờ có thể chắc chắn liệu ai đó đã ghi đè hàm để trả về một thứ khác hay không…) hoặc có thể viết một bảng chứa các hằng số, như được đề xuất ở đây . Có lẽ viết một trình kích hoạt quay lại bất kỳ thay đổi nào đối với ConstantValue
cột?
Trước khi sử dụng một hàm SQL, hãy chạy tập lệnh sau để xem sự khác biệt về hiệu suất:
IF OBJECT_ID('fnFalse') IS NOT NULL
DROP FUNCTION fnFalse
GO
IF OBJECT_ID('fnTrue') IS NOT NULL
DROP FUNCTION fnTrue
GO
CREATE FUNCTION fnTrue() RETURNS INT WITH SCHEMABINDING
AS
BEGIN
RETURN 1
END
GO
CREATE FUNCTION fnFalse() RETURNS INT WITH SCHEMABINDING
AS
BEGIN
RETURN ~ dbo.fnTrue()
END
GO
DECLARE @TimeStart DATETIME = GETDATE()
DECLARE @Count INT = 100000
WHILE @Count > 0 BEGIN
SET @Count -= 1
DECLARE @Value BIT
SELECT @Value = dbo.fnTrue()
IF @Value = 1
SELECT @Value = dbo.fnFalse()
END
DECLARE @TimeEnd DATETIME = GETDATE()
PRINT CAST(DATEDIFF(ms, @TimeStart, @TimeEnd) AS VARCHAR) + ' elapsed, using function'
GO
DECLARE @TimeStart DATETIME = GETDATE()
DECLARE @Count INT = 100000
DECLARE @FALSE AS BIT = 0
DECLARE @TRUE AS BIT = ~ @FALSE
WHILE @Count > 0 BEGIN
SET @Count -= 1
DECLARE @Value BIT
SELECT @Value = @TRUE
IF @Value = 1
SELECT @Value = @FALSE
END
DECLARE @TimeEnd DATETIME = GETDATE()
PRINT CAST(DATEDIFF(ms, @TimeStart, @TimeEnd) AS VARCHAR) + ' elapsed, using local variable'
GO
DECLARE @TimeStart DATETIME = GETDATE()
DECLARE @Count INT = 100000
WHILE @Count > 0 BEGIN
SET @Count -= 1
DECLARE @Value BIT
SELECT @Value = 1
IF @Value = 1
SELECT @Value = 0
END
DECLARE @TimeEnd DATETIME = GETDATE()
PRINT CAST(DATEDIFF(ms, @TimeStart, @TimeEnd) AS VARCHAR) + ' elapsed, using hard coded values'
GO
2760ms elapsed, using function
| 2300ms elapsed, using local variable
| 2286ms elapsed, using hard coded values
|
5570 elapsed, using function
| 406 elapsed, using local variable
| 383 elapsed, using hard coded values
| 3893 elapsed, using function without schemabinding
select top 1 @m = cv_val from code_values where cv_id = 'C101'
và giống nhau ... 'C201'
trong đó code_values là bảng từ điển với 250 vars, tất cả đều có trên SQL-Server 2016
Nếu bạn quan tâm đến việc có được kế hoạch thực thi tối ưu cho một giá trị trong biến, bạn có thể sử dụng mã sql động. Nó làm cho biến không đổi.
DECLARE @var varchar(100) = 'some text'
DECLARE @sql varchar(MAX)
SET @sql = 'SELECT * FROM table WHERE col = '''+@var+''''
EXEC (@sql)
Đối với enums hoặc hằng số đơn giản, chế độ xem có một hàng có hiệu suất tuyệt vời và kiểm tra thời gian biên dịch / theo dõi phụ thuộc (gây ra tên cột của nó)
Xem bài đăng trên blog của Jared Ko https://blogs.msdn.microsoft.com/sql_server_append_z/2013/09/16/sql-server-variables-parameters-or-literals-or-constants/
tạo khung nhìn
CREATE VIEW ShipMethods AS
SELECT CAST(1 AS INT) AS [XRQ - TRUCK GROUND]
,CAST(2 AS INT) AS [ZY - EXPRESS]
,CAST(3 AS INT) AS [OVERSEAS - DELUXE]
, CAST(4 AS INT) AS [OVERNIGHT J-FAST]
,CAST(5 AS INT) AS [CARGO TRANSPORT 5]
sử dụng chế độ xem
SELECT h.*
FROM Sales.SalesOrderHeader
WHERE ShipMethodID = ( select [OVERNIGHT J-FAST] from ShipMethods )
Được rồi, hãy xem
Hằng số là các giá trị bất biến được biết đến tại thời điểm biên dịch và không thay đổi đối với vòng đời của chương trình
điều đó có nghĩa là bạn không bao giờ có thể có một hằng số trong SQL Server
declare @myvalue as int
set @myvalue = 5
set @myvalue = 10--oops we just changed it
giá trị vừa thay đổi
Vì không có bản dựng hỗ trợ cho các hằng số, giải pháp của tôi rất đơn giản.
Vì điều này không được hỗ trợ:
Declare Constant @supplement int = 240
SELECT price + @supplement
FROM what_does_it_cost
Tôi chỉ cần chuyển đổi nó thành
SELECT price + 240/*CONSTANT:supplement*/
FROM what_does_it_cost
Rõ ràng, điều này dựa vào toàn bộ điều (giá trị không có dấu cách ở cuối và chú thích) là duy nhất. Có thể thay đổi nó bằng cách tìm kiếm và thay thế toàn cầu.
Không có cái gọi là "tạo một hằng số" trong tài liệu cơ sở dữ liệu. Hằng số tồn tại như chúng vốn có và thường được gọi là giá trị. Người ta có thể khai báo một biến và gán một giá trị (hằng số) cho nó. Từ một cái nhìn học thuật:
DECLARE @two INT
SET @two = 2
Ở đây @two là một biến và 2 là một giá trị / hằng số.
2
được dịch thành giá trị nhị phân khi được gán vào "thời gian biên dịch". Giá trị thực được mã hóa phụ thuộc vào kiểu dữ liệu mà nó đang được gán (int, char, ...).
Câu trả lời tốt nhất là từ SQLMenace theo yêu cầu nếu đó là tạo một hằng số tạm thời để sử dụng trong các tập lệnh, tức là trên nhiều câu lệnh / lô GO.
Chỉ cần tạo thủ tục trong tempdb thì bạn không có tác động nào đến cơ sở dữ liệu đích.
Một ví dụ thực tế về điều này là một tập lệnh tạo cơ sở dữ liệu ghi một giá trị điều khiển vào cuối tập lệnh chứa phiên bản lược đồ logic. Ở đầu tệp là một số nhận xét với lịch sử thay đổi, v.v ... Nhưng trên thực tế, hầu hết các nhà phát triển sẽ quên cuộn xuống và cập nhật phiên bản lược đồ ở cuối tệp.
Sử dụng mã trên cho phép một hằng số phiên bản lược đồ hiển thị được xác định ở trên cùng trước khi tập lệnh cơ sở dữ liệu (được sao chép từ tính năng tập lệnh tạo của SSMS) tạo cơ sở dữ liệu nhưng được sử dụng ở cuối. Điều này đúng đối mặt với nhà phát triển bên cạnh lịch sử thay đổi và các nhận xét khác, vì vậy họ rất có thể sẽ cập nhật nó.
Ví dụ:
use tempdb
go
create function dbo.MySchemaVersion()
returns int
as
begin
return 123
end
go
use master
go
-- Big long database create script with multiple batches...
print 'Creating database schema version ' + CAST(tempdb.dbo.MySchemaVersion() as NVARCHAR) + '...'
go
-- ...
go
-- ...
go
use MyDatabase
go
-- Update schema version with constant at end (not normally possible as GO puts
-- local @variables out of scope)
insert MyConfigTable values ('SchemaVersion', tempdb.dbo.MySchemaVersion())
go
-- Clean-up
use tempdb
drop function MySchemaVersion
go
WITH SCHEMABINDING
nên biến điều này thành một hằng số 'thực' (một yêu cầu đối với một UDF được coi là xác định trong SQL). Tức là nó sẽ được lưu vào bộ nhớ đệm. Tuy nhiên, +1.