TSQL değişkenini sabit yapmanın bir yolu var mı?


Yanıtlar:


60

Hayır, ancak bir işlev oluşturabilir ve onu orada kodlayabilir ve onu kullanabilirsiniz.

İşte bir örnek:

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

SELECT dbo.fnConstant()

13
WITH SCHEMABINDING bunu 'gerçek' bir sabite dönüştürmelidir (bir UDF'nin SQL'de deterministik olarak görülmesi için bir gereklilik). Yani önbelleğe alınmalı. Yine de +1.
Jonathan Dickinson

bu cevap iyi, sadece merak ediyorum sqlserver'daki tablo sütunları bir işleve varsayılan değer olarak başvurabilir. bunu işe alamadım
Ab Bennett

1
@JonathanDickinson Açık olmak gerekirse, Öneriniz kullanmaktır WITH SCHEMABINDINGiçinde CREATE FUNCTION(function arayabilecek olan bir saklı yordam içinde aksine) deyimi - Bu doğru mu?
Bütünsel Geliştirici

1
Evet, işlevde. O kadar - İLE SCHEMABINDING SQL "inlined tablo değerli işlevler" satır içi sağlayan da : Bu formda olması gerekiyor gist.github.com/jcdickinson/61a38dedb84b35251da301b128535ceb . Sorgu analizcisi, SCHEMABINDING olmadan hiçbir şeyi veya BEGIN ile herhangi bir şeyi satır içi yapmaz.
Jonathan Dickinson

Belirleyici olmayan UDF'leri kullanmanın sonuçları: docs.microsoft.com/es-es/archive/blogs/sqlprogrammability/…
Ochoto

28

Jared Ko tarafından sunulan bir çözüm, sözde sabitleri kullanmaktır .

SQL Server'da açıklandığı gibi : Değişkenler, Parametreler veya Değişkenler? Veya… Sabitler? :

Sözde Sabitler, değişkenler veya parametreler değildir. Bunun yerine, tek satırlı görünümler ve sabitlerinizi desteklemek için yeterli sütunlardır. Bu basit kurallarla SQL Engine, görünümün değerini tamamen yok sayar, ancak yine de değerine dayalı olarak bir yürütme planı oluşturur. Uygulama planı, görünüme bir katılım bile göstermiyor!

Bunun gibi oluşturun:

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]

O zaman şu şekilde kullanın:

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

Veya bunun gibi:

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

1
Bu, kabul edilen cevaptan ÇOK daha iyi bir çözümdür. Başlangıçta skaler fonksiyon rotasına gittik ve korkunç bir performansa sahip. Bu yanıt ve yukarıdaki Jared Ko'nun makalesine bağlantı çok daha iyi.
David Coster

Ancak, bir skaler işleve ŞEMA BAĞLANTISIYLA eklemek, performansını önemli ölçüde artırıyor gibi görünüyor.
David Coster

Bağlantı artık öldü.
Matthieu Cormier

1
@MatthieuCormier: Bağlantıyı güncelledim, ancak MSDN eski URL'den yenisine bir yönlendirme eklemiş gibi görünüyor.
Ilmari Karonen

23

Eksik sabitler için çözümüm, optimize ediciye değer hakkında ipuçları vermektir.

DECLARE @Constant INT = 123;

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

Bu, sorgu derleyicisine, değişkene yürütme planını oluştururken sabitmiş gibi davranmasını söyler. Olumsuz tarafı, değeri iki kez tanımlamanız gerektiğidir.


3
Yardımcı olur ama aynı zamanda tek bir tanımın amacını da ortadan kaldırır.
MikeJRamsey56

10

Hayır, ancak eski güzel adlandırma kuralları kullanılmalıdır.

declare @MY_VALUE as int

@VictorYarema, çünkü bazen tek ihtiyacınız olan kongre. Ve bazen başka iyi seçeneğiniz olmadığı için. Şimdi, bunun yanı sıra, SQLMenace'in cevabı daha iyi görünüyor, size katılıyorum. Öyle olsa bile, işlev adı sabitler için IMO kuralına uymalıdır. Adlandırılmalıdır FN_CONSTANT(). Bu şekilde ne yaptığı açıktır.
tfrascaroli

Bu tek başına performans avantajı istediğinizde yardımcı olmaz. Michal D. ve John Nilsson'un performans artışı için cevaplarını da deneyin.
WonderWorker

8

T-SQL'de sabitler için yerleşik destek yoktur. Simüle etmek için SQLMenace'in yaklaşımını kullanabilirsiniz (yine de başka birinin başka bir şey döndürmek için işlevin üzerine yazıp yazmadığından emin olamazsınız…) veya burada önerildiği gibi sabitler içeren bir tablo yazabilirsiniz . ConstantValueSütunda herhangi bir değişikliği geri alan bir tetikleyici yazabilir misiniz?


7

Bir SQL işlevini kullanmadan önce, performanstaki farklılıkları görmek için aşağıdaki komut dosyasını çalıştırın:

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
Bu oldukça eskidir, ancak referans için sunucumda çalıştırıldığında sonuç şu şekildedir: | 2760ms elapsed, using function| 2300ms elapsed, using local variable| 2286ms elapsed, using hard coded values|
z00l

2
Şema bağlamasız iki ek işlevle geliştirici bir dizüstü bilgisayarda. 5570 elapsed, using function | 406 elapsed, using local variable| 383 elapsed, using hard coded values| 3893 elapsed, using function without schemabinding
monkeyhouse

Karşılaştırma için, basit bir seçme ifadesi 4110 ms sürdü, burada kod_değerleri 250 değişkenli sözlük tablosu olduğunda , seçim ifadeleri arasında değişiyordu select top 1 @m = cv_val from code_values where cv_id = 'C101' ve ... 'C201' hepsi SQL-Server 2016'da vardı
monkeyhouse

6

Değişkendeki bir değer için en uygun yürütme planını almakla ilgileniyorsanız, dinamik bir sql kodu kullanabilirsiniz. Değişkeni sabit yapar.

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

1
Ben böyle yaparım ve sabitleri içeren sorgulara büyük bir performans artışı sağlar.
WonderWorker

5

Numaralandırmalar veya basit sabitler için, tek satırlı bir görünüm müthiş bir performansa sahiptir ve derleme zamanı denetimi / bağımlılık takibi sağlar (çünkü bir sütun adıdır)

Jared Ko'nun blog gönderisine bakın https://blogs.msdn.microsoft.com/sql_server_appendix_z/2013/09/16/sql-server-variables-parameters-or-literals-or-constants/

görünümü yarat

 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]

görünümü kullan

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

3

Tamam, görelim

Sabitler, derleme sırasında bilinen ve programın ömrü boyunca değişmeyen değişmez değerlerdir.

bu, SQL Server'da asla bir sabite sahip olamayacağınız anlamına gelir

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

değer az önce değişti


1

Sabitler için hiçbir yapı desteği olmadığından, çözümüm çok basit.

Bu desteklenmediğinden:

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

Ben onu basitçe

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

Açıkçası, bu her şeyin (boşluk ve yorumun arkasına gitmeyen değer) benzersiz olmasına dayanır. Küresel arama ve değiştirme ile değiştirmek mümkündür.


Sorunlardan biri, yalnızca yerel olarak mevcut olması
Bernardo Dal Corno

0

Veritabanı literatüründe "sabit oluşturmak" diye bir şey yoktur. Sabitler oldukları gibi var olurlar ve genellikle değerler olarak adlandırılırlar. Bir değişken tanımlayabilir ve ona bir değer (sabit) atayabilir. Eğitimsel bir bakış açısıyla:

DECLARE @two INT
SET @two = 2

Burada @ iki bir değişkendir ve 2 bir değer / sabittir.


Performans artışı için Michal D. ve John Nilsson'un cevaplarını deneyin.
WonderWorker

Değişmezler tanım gereği sabittir. Ascii / unicode (düzenleyiciye bağlı olarak) karakteri 2, "derleme zamanında" atandığında ikili bir değere çevrilir. Kodlanan gerçek değer, atandığı veri türüne bağlıdır (int, char, ...).
samis

-1

En iyi yanıt, komut dosyalarında, yani birden çok GO deyimi / toplu işinde kullanılmak üzere geçici bir sabit oluşturmaksa gereksinime göre SQLMenace'den alınır.

Sadece tempdb'de yordamı oluşturun, ardından hedef veritabanı üzerinde hiçbir etkiniz olmaz.

Bunun pratik bir örneği, mantıksal şema sürümünü içeren komut dosyasının sonuna bir kontrol değeri yazan bir veritabanı oluşturma betiğidir. Dosyanın en üstünde, değişiklik geçmişi vb. İçeren bazı yorumlar var ... Ancak pratikte çoğu geliştirici, aşağı kaydırmayı ve dosyanın altındaki şema sürümünü güncellemeyi unutacaktır.

Yukarıdaki kodun kullanılması, görünür bir şema sürüm sabitinin, veritabanı komut dosyası (SSMS'nin komut dosyası oluşturma özelliğinden kopyalanan) veritabanını oluşturmadan önce en üstte tanımlanmasına izin verir, ancak sonunda kullanılır. Bu, değişiklik geçmişinin ve diğer yorumların hemen yanında geliştiricinin karşısındadır, bu nedenle güncelleme olasılıkları çok yüksektir.

Örneğin:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.