Gibt es eine Möglichkeit, eine TSQL-Variable konstant zu machen?
Gibt es eine Möglichkeit, eine TSQL-Variable konstant zu machen?
Antworten:
Nein, aber Sie können eine Funktion erstellen und dort fest codieren und verwenden.
Hier ist ein Beispiel:
CREATE FUNCTION fnConstant()
RETURNS INT
AS
BEGIN
RETURN 2
END
GO
SELECT dbo.fnConstant()
WITH SCHEMABINDING
in der CREATE FUNCTION
Anweisung zu verwenden (im Gegensatz zu einer gespeicherten Prozedur, die möglicherweise die Funktion aufruft) - ist das richtig?
Eine von Jared Ko angebotene Lösung ist die Verwendung von Pseudokonstanten .
Wie in SQL Server erläutert : Variablen, Parameter oder Literale? Oder ... Konstanten? ::
Pseudokonstanten sind keine Variablen oder Parameter. Stattdessen handelt es sich lediglich um Ansichten mit einer Zeile und genügend Spalten, um Ihre Konstanten zu unterstützen. Mit diesen einfachen Regeln ignoriert die SQL Engine den Wert der Ansicht vollständig, erstellt jedoch weiterhin einen Ausführungsplan basierend auf ihrem Wert. Der Ausführungsplan zeigt nicht einmal einen Join zur Ansicht!
Erstellen Sie wie folgt:
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]
Dann verwenden Sie wie folgt:
SELECT h.* FROM Sales.SalesOrderHeader h JOIN ShipMethod.ShipMethodID const ON h.ShipMethodID = const.[OVERNIGHT J-FAST]
Oder so:
SELECT h.* FROM Sales.SalesOrderHeader h WHERE h.ShipMethodID = (SELECT TOP 1 [OVERNIGHT J-FAST] FROM ShipMethod.ShipMethodID)
Meine Problemumgehung für fehlende Konstanten besteht darin, dem Optimierer Hinweise zum Wert zu geben.
DECLARE @Constant INT = 123;
SELECT *
FROM [some_relation]
WHERE [some_attribute] = @Constant
OPTION( OPTIMIZE FOR (@Constant = 123))
Dies weist den Abfrage-Compiler an, die Variable beim Erstellen des Ausführungsplans so zu behandeln, als wäre sie eine Konstante. Der Nachteil ist, dass Sie den Wert zweimal definieren müssen.
Nein, aber es sollten gute alte Namenskonventionen verwendet werden.
declare @MY_VALUE as int
FN_CONSTANT()
. Auf diese Weise ist klar, was es tut.
Es gibt keine integrierte Unterstützung für Konstanten in T-SQL. Sie können den SQLMenace-Ansatz verwenden, um ihn zu simulieren (obwohl Sie nie sicher sein können, ob jemand anderes die Funktion überschrieben hat, um etwas anderes zurückzugeben ...) oder möglicherweise eine Tabelle mit Konstanten schreiben, wie hier vorgeschlagen . Vielleicht einen Trigger schreiben, der alle Änderungen an der ConstantValue
Spalte rückgängig macht?
Führen Sie vor der Verwendung einer SQL-Funktion das folgende Skript aus, um die Leistungsunterschiede festzustellen:
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'
und dieselbe wechselten, ... 'C201'
wobei code_values eine Wörterbuchtabelle mit 250 Variablen ist. Alle waren auf SQL-Server 2016
Wenn Sie einen optimalen Ausführungsplan für einen Wert in der Variablen erhalten möchten, können Sie einen dynamischen SQL-Code verwenden. Es macht die Variable konstant.
DECLARE @var varchar(100) = 'some text'
DECLARE @sql varchar(MAX)
SET @sql = 'SELECT * FROM table WHERE col = '''+@var+''''
EXEC (@sql)
Bei Aufzählungen oder einfachen Konstanten bietet eine Ansicht mit einer einzelnen Zeile eine hervorragende Leistung und Überprüfung der Kompilierungszeit / Abhängigkeitsverfolgung (da es sich um einen Spaltennamen handelt).
Siehe den Blog-Beitrag von Jared Ko unter https://blogs.msdn.microsoft.com/sql_server_appendix_z/2013/09/16/sql-server-variables-parameters-or-literals-or-constants/
Erstellen Sie die Ansicht
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]
Verwenden Sie die Ansicht
SELECT h.*
FROM Sales.SalesOrderHeader
WHERE ShipMethodID = ( select [OVERNIGHT J-FAST] from ShipMethods )
Okay, mal sehen
Konstanten sind unveränderliche Werte, die zur Kompilierungszeit bekannt sind und sich während der Laufzeit des Programms nicht ändern
Das bedeutet, dass Sie in SQL Server niemals eine Konstante haben können
declare @myvalue as int
set @myvalue = 5
set @myvalue = 10--oops we just changed it
Der Wert hat sich gerade geändert
Da es keine eingebaute Unterstützung für Konstanten gibt, ist meine Lösung sehr einfach.
Da dies nicht unterstützt wird:
Declare Constant @supplement int = 240
SELECT price + @supplement
FROM what_does_it_cost
Ich würde es einfach umwandeln
SELECT price + 240/*CONSTANT:supplement*/
FROM what_does_it_cost
Dies hängt natürlich davon ab, dass das Ganze (der Wert ohne Leerzeichen und der Kommentar) eindeutig ist. Das Ändern ist mit einem globalen Suchen und Ersetzen möglich.
In der Datenbankliteratur gibt es kein "Erstellen einer Konstante". Konstanten existieren so wie sie sind und werden oft als Werte bezeichnet. Man kann eine Variable deklarieren und ihr einen Wert (Konstante) zuweisen. Aus schulischer Sicht:
DECLARE @two INT
SET @two = 2
Hier ist @two eine Variable und 2 ist ein Wert / eine Konstante.
2
wird bei Zuweisung zur "Kompilierungszeit" in einen Binärwert übersetzt. Der tatsächlich codierte Wert hängt vom Datentyp ab, dem er zugewiesen ist (int, char, ...).
Die beste Antwort kommt von SQLMenace, je nachdem, ob eine temporäre Konstante für die Verwendung in Skripten erstellt werden soll, dh über mehrere GO-Anweisungen / Stapel hinweg.
Erstellen Sie einfach die Prozedur in der Tempdb, dann haben Sie keine Auswirkungen auf die Zieldatenbank.
Ein praktisches Beispiel hierfür ist ein Datenbankerstellungsskript, das am Ende des Skripts, das die logische Schemaversion enthält, einen Steuerwert schreibt. Am Anfang der Datei befinden sich einige Kommentare mit Änderungsverlauf usw. In der Praxis werden die meisten Entwickler jedoch vergessen, nach unten zu scrollen und die Schemaversion am Ende der Datei zu aktualisieren.
Mit dem obigen Code kann oben eine sichtbare Schema-Versionskonstante definiert werden, bevor das Datenbankskript (kopiert von der Funktion zum Generieren von Skripten von SSMS) die Datenbank erstellt, aber am Ende verwendet wird. Dies steht dem Entwickler direkt neben dem Änderungsverlauf und anderen Kommentaren gegenüber, sodass es sehr wahrscheinlich ist, dass er aktualisiert wird.
Beispielsweise:
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
sollte dies in eine 'echte' Konstante umwandeln (eine Voraussetzung dafür, dass eine UDF in SQL als deterministisch angesehen wird). Dh es sollte landen, wenn es zwischengespeichert wird. Trotzdem +1.