Apakah ada cara untuk membuat variabel TSQL konstan?


Jawaban:


60

Tidak, tetapi Anda dapat membuat fungsi dan membuat kode keras di sana dan menggunakannya.

Berikut ini contohnya:

CREATE FUNCTION fnConstant()
RETURNS INT
AS
BEGIN
    RETURN 2
END
GO

SELECT dbo.fnConstant()

13
WITH SCHEMABINDING harus mengubahnya menjadi konstanta 'nyata' (persyaratan untuk UDF untuk dilihat sebagai deterministik dalam SQL). Yaitu harus mendarat di cache. Tetap saja, +1.
Jonathan Dickinson

jawaban ini bagus, hanya penasaran dapat kolom tabel di sqlserver mereferensikan fungsi sebagai nilai default. saya tidak bisa mendapatkan ini untuk bekerja
Ab Bennett

1
@JonathanDickinson Agar jelas, saran Anda adalah untuk digunakan WITH SCHEMABINDINGdalam CREATE FUNCTIONpernyataan (sebagai lawan dalam prosedur tersimpan yang mungkin memanggil fungsi) - apakah itu benar?
Pengembang Holistik

1
Ya, dalam fungsinya. DENGAN SCHEMABINDING memungkinkan SQL untuk menyebariskan "fungsi nilai tabel sebaris" - jadi SQL juga harus dalam bentuk ini: gist.github.com/jcdickinson/61a38dedb84b35251da301b128535ceb . Penganalisis kueri tidak akan menyebariskan apa pun tanpa SCHEMABINDING atau apa pun dengan BEGIN.
Jonathan Dickinson

Implikasi penggunaan UDF non deterministik: docs.microsoft.com/es-es/archive/blogs/sqlprogrammability/…
Ochoto

28

Salah satu solusi yang ditawarkan Jared Ko adalah dengan menggunakan konstanta semu .

Seperti yang dijelaskan di SQL Server: Variabel, Parameter, atau Literal? Atau… Konstanta? :

Pseudo-Constants bukanlah variabel atau parameter. Sebaliknya, mereka hanya dilihat dengan satu baris, dan kolom yang cukup untuk mendukung konstanta Anda. Dengan aturan sederhana ini, SQL Engine sepenuhnya mengabaikan nilai tampilan tetapi masih membuat rencana eksekusi berdasarkan nilainya. Rencana eksekusi bahkan tidak menunjukkan gabungan tampilan!

Buat seperti ini:

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]

Kemudian gunakan seperti ini:

SELECT h.*
FROM Sales.SalesOrderHeader h
JOIN ShipMethod.ShipMethodID const
    ON h.ShipMethodID = const.[OVERNIGHT J-FAST]

Atau seperti ini:

SELECT h.*
FROM Sales.SalesOrderHeader h
WHERE h.ShipMethodID = (SELECT TOP 1 [OVERNIGHT J-FAST] FROM ShipMethod.ShipMethodID)

1
Ini adalah solusi yang JAUH lebih baik daripada jawaban yang diterima. Awalnya kami mengikuti rute fungsi skalar dan memiliki kinerja yang buruk. Jauh lebih baik jawaban ini dan tautan di atas ke artikel Jared Ko.
David Coster

Namun, menambahkan WITH SCHEMABINDING ke fungsi skalar tampaknya meningkatkan kinerjanya secara signifikan.
David Coster

Tautannya sudah mati sekarang.
Matthieu Cormier

1
@MatthieuCormier: Saya telah memperbarui tautan, meskipun tampaknya MSDN telah menambahkan pengalihan dari URL lama ke yang baru.
Ilmari Karonen

23

Solusi saya untuk kehilangan konstanta adalah memberikan petunjuk tentang nilai kepada pengoptimal.

DECLARE @Constant INT = 123;

SELECT * 
FROM [some_relation] 
WHERE [some_attribute] = @Constant
OPTION( OPTIMIZE FOR (@Constant = 123))

Ini memberi tahu compiler kueri untuk memperlakukan variabel seolah-olah itu adalah konstanta saat membuat rencana eksekusi. Sisi negatifnya adalah Anda harus mendefinisikan nilainya dua kali.


3
Ini membantu tetapi juga mengalahkan tujuan definisi tunggal.
MikeJRamsey56

10

Tidak, tapi konvensi penamaan lama yang baik harus digunakan.

declare @MY_VALUE as int

@VictorYarema karena terkadang hanya konvensi yang Anda butuhkan. Dan karena terkadang Anda tidak punya pilihan lain yang baik. Sekarang, selain itu, jawaban SQLMenace terlihat lebih baik, saya setuju dengan Anda. Meski begitu, nama fungsi harus mengikuti konvensi konstanta, IMO. Ini harus diberi nama FN_CONSTANT(). Dengan begitu, jelas apa yang dilakukannya.
tfrascaroli

Ini saja tidak akan membantu bila Anda menginginkan manfaat kinerja. Coba juga jawaban Michal D. dan John Nilsson untuk peningkatan kinerja.
WonderWorker

8

Tidak ada dukungan bawaan untuk konstanta di T-SQL. Anda bisa menggunakan pendekatan SQLMenace untuk mensimulasikannya (meskipun Anda tidak pernah bisa yakin apakah orang lain telah menimpa fungsi untuk mengembalikan sesuatu yang lain…), atau mungkin menulis tabel yang berisi konstanta, seperti yang disarankan di sini . Mungkin menulis pemicu yang mengembalikan setiap perubahan ke ConstantValuekolom?


7

Sebelum menggunakan fungsi SQL, jalankan skrip berikut untuk melihat perbedaan kinerja:

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
Ini sudah cukup lama, tetapi untuk referensi inilah hasilnya saat dijalankan di server saya: | 2760ms elapsed, using function| 2300ms elapsed, using local variable| 2286ms elapsed, using hard coded values|
z00l

2
Di laptop pengembang, dengan dua fungsi tambahan tanpa pengikatan skema. 5570 elapsed, using function | 406 elapsed, using local variable| 383 elapsed, using hard coded values| 3893 elapsed, using function without schemabinding
monkeyhouse

Sebagai perbandingan, pernyataan pilih sederhana membutuhkan waktu 4110 md di mana pernyataan pilih bergantian antara select top 1 @m = cv_val from code_values where cv_id = 'C101' dan sama di ... 'C201' mana code_values ​​adalah tabel kamus dengan 250 vars, semuanya ada di SQL-Server 2016
monkeyhouse

6

Jika Anda tertarik untuk mendapatkan rencana eksekusi yang optimal untuk nilai dalam variabel, Anda dapat menggunakan kode sql dinamis. Itu membuat variabel konstan.

DECLARE @var varchar(100) = 'some text'
DECLARE @sql varchar(MAX)
SET @sql = 'SELECT * FROM table WHERE col = '''+@var+''''
EXEC (@sql)

1
Ini adalah cara saya melakukannya dan ini memberikan peningkatan kinerja yang sangat besar pada kueri yang melibatkan konstanta.
WonderWorker

5

Untuk enum atau konstanta sederhana, tampilan dengan satu baris memiliki performa yang bagus dan pemeriksaan waktu kompilasi / pelacakan ketergantungan (karena itu adalah nama kolom)

Lihat entri blog Jared Ko https://blogs.msdn.microsoft.com/sql_server_appendix_z/2013/09/16/sql-server-variables-parameters-or-literals-or-constants/

buat tampilan

 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]

gunakan tampilan

SELECT h.*
FROM Sales.SalesOrderHeader 
WHERE ShipMethodID = ( select [OVERNIGHT J-FAST] from ShipMethods  )

3

Oke, mari kita lihat

Konstanta adalah nilai yang tidak dapat diubah yang diketahui pada waktu kompilasi dan tidak berubah selama masa pakai program

itu berarti Anda tidak akan pernah memiliki konstanta di SQL Server

declare @myvalue as int
set @myvalue = 5
set @myvalue = 10--oops we just changed it

nilainya baru saja berubah


1

Karena tidak ada build yang mendukung konstanta, solusi saya sangat sederhana.

Karena ini tidak didukung:

Declare Constant @supplement int = 240
SELECT price + @supplement
FROM   what_does_it_cost

Saya hanya akan mengubahnya menjadi

SELECT price + 240/*CONSTANT:supplement*/
FROM   what_does_it_cost

Jelas, ini bergantung pada semuanya (nilai tanpa spasi dan komentar) agar unik. Mengubahnya dimungkinkan dengan pencarian dan penggantian global.


Satu masalah adalah bahwa itu hanya tersedia secara lokal
Bernardo Dal Corno

0

Tidak ada hal seperti "membuat konstanta" dalam literatur database. Konstanta ada sebagaimana adanya dan sering disebut nilai. Seseorang dapat mendeklarasikan sebuah variabel dan menetapkan nilai (konstanta) padanya. Dari pandangan skolastik:

DECLARE @two INT
SET @two = 2

Di sini @two adalah variabel dan 2 adalah nilai / konstanta.


Coba juga jawaban Michal D. dan John Nilsson untuk meningkatkan kinerja.
WonderWorker

Literal konstan menurut definisi. Karakter ascii / unicode (bergantung pada editor) 2diterjemahkan ke dalam nilai biner saat ditetapkan pada "waktu kompilasi". Nilai sebenarnya yang dikodekan bergantung pada tipe data yang ditugaskan (int, char, ...).
samis

-1

Jawaban terbaik adalah dari SQLMenace sesuai dengan persyaratan jika itu adalah untuk membuat konstanta sementara untuk digunakan dalam skrip, yaitu di beberapa pernyataan / batch GO.

Buat saja prosedur di tempdb maka Anda tidak berdampak pada database target.

Salah satu contoh praktisnya adalah skrip buat basis data yang menulis nilai kontrol di akhir skrip yang berisi versi skema logis. Di bagian atas file terdapat beberapa komentar dengan riwayat perubahan, dll. Tetapi dalam praktiknya sebagian besar pengembang akan lupa untuk menggulir ke bawah dan memperbarui versi skema di bagian bawah file.

Menggunakan kode di atas memungkinkan konstanta versi skema yang terlihat untuk didefinisikan di atas sebelum skrip database (disalin dari fitur skrip pembuatan SSMS) membuat database tetapi digunakan di bagian akhir. Ini tepat di hadapan pengembang di samping riwayat perubahan dan komentar lain, jadi mereka sangat mungkin untuk memperbaruinya.

Sebagai contoh:

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.