Có cách nào để làm cho biến TSQL không đổi?


Câu trả lời:


60

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()

13
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.
Jonathan Dickinson

câu trả lời này là tốt, chỉ cần tò mò có thể bảng các cột trong sqlserver tham chiếu một hàm như một giá trị mặc định. tôi không thể có được điều này để làm việc
Ab Bennett

1
@JonathanDickinson Để rõ ràng, đề xuất của bạn là sử dụng WITH SCHEMABINDINGtrong CREATE FUNCTIONcâ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?
Nhà phát triển toàn diện

1
Có, trong chức năng. VỚI SCHEMABINDING cho phép SQL nội tuyến "các hàm có giá trị bảng nội tuyến" - vì vậy nó cũng cần phải ở dạng sau: gist.github.com/jcdickinson/61a38dedb84b35251da301b128535ceb . Trình phân tích truy vấn sẽ không nội tuyến bất kỳ thứ gì nếu không có SCHEMABINDING hoặc bất kỳ thứ gì có BEGIN.
Jonathan Dickinson


28

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)

1
Đây là một giải pháp tốt hơn rất nhiều so với câu trả lời được chấp nhận. Ban đầu, chúng tôi đã đi xuống tuyến hàm vô hướng và nó có hiệu suất khủng khiếp. Tốt hơn nhiều là câu trả lời này và liên kết ở trên đến bài viết của Jared Ko.
David Coster

Tuy nhiên, việc thêm WITH SCHEMABINDING vào một hàm vô hướng dường như cải thiện đáng kể hiệu suất của nó.
David Coster

Liên kết đã chết.
Matthieu Cormier

1
@MatthieuCormier: Tôi đã cập nhật liên kết, mặc dù có vẻ như MSDN đã thêm chuyển hướng từ URL cũ sang URL mới.
Ilmari Karonen

23

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.


3
Nó giúp ích nhưng nó cũng đánh bại mục đích của một định nghĩa duy nhất.
MikeJRamsey56

9

Không, nhưng nên sử dụng các quy ước đặt tên cũ tốt.

declare @MY_VALUE as int

@VictorYarema vì đôi khi quy ước là tất cả những gì bạn cần. Và bởi vì đôi khi bạn không có lựa chọn tốt nào khác. Bây giờ, điều đó sang một bên, câu trả lời của SQLMenace có vẻ tốt hơn, tôi sẽ đồng ý với bạn. Mặc dù vậy, tên hàm nên tuân theo quy ước về hằng số, IMO. Nó nên được đặt tên FN_CONSTANT(). Bằng cách đó, rõ ràng nó đang làm gì.
tfrascaroli

Chỉ điều này sẽ không hữu ích khi bạn muốn lợi ích về hiệu suất. Hãy thử câu trả lời của Michal D. và John Nilsson để tăng hiệu suất.
WonderWorker

8

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 ConstantValuecột?


7

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

4
Điều này khá cũ, nhưng để tham khảo, đây là kết quả khi được thực thi trên máy chủ của tôi: | 2760ms elapsed, using function| 2300ms elapsed, using local variable| 2286ms elapsed, using hard coded values|
z00l

2
Trên máy tính xách tay dành cho nhà phát triển, với hai chức năng bổ sung mà không có liên kết lược đồ. 5570 elapsed, using function | 406 elapsed, using local variable| 383 elapsed, using hard coded values| 3893 elapsed, using function without schemabinding
Monkeyhouse

Để so sánh, một câu lệnh select đơn giản mất 4110ms trong đó các câu lệnh select xen kẽ giữa 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
Monkeyhouse

6

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)

1
Đây là cách tôi thực hiện và nó giúp tăng hiệu suất rất lớn cho các truy vấn liên quan đến hằng số.
WonderWorker

5

Đố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  )

3

Đượ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


1

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.


Một vấn đề là nó chỉ có ở địa phương
Bernardo Dal Corno

0

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ố.


Hãy thử các câu trả lời của Michal D. và John Nilsson để tăng hiệu suất.
WonderWorker

Chữ nghĩa là không đổi theo định nghĩa. Ký tự ascii / unicode (tùy thuộc vào trình soạn thảo) 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, ...).
samis

-1

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
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.