TSQL変数を定数にする方法はありますか?
TSQL変数を定数にする方法はありますか?
回答:
いいえ。ただし、関数を作成してそこにハードコーディングして使用することはできます。
次に例を示します。
CREATE FUNCTION fnConstant()
RETURNS INT
AS
BEGIN
RETURN 2
END
GO
SELECT dbo.fnConstant()
WITH SCHEMABINDING
にはCREATE FUNCTION
(関数を呼び出すことがありますストアドプロシージャではなく)声明-その権利がありますか?
Jared Koが提供する1つの解決策は、疑似定数を使用することです。
SQL Serverで説明されているように:変数、パラメーター、またはリテラル?または…定数?:
疑似定数は変数またはパラメーターではありません。代わりに、それらは1つの行と、定数をサポートするのに十分な列を持つ単純なビューです。これらの単純なルールを使用すると、SQLエンジンはビューの値を完全に無視しますが、その値に基づいて実行プランを作成します。実行プランには、ビューへの結合すら表示されません。
次のように作成します。
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]
次に、次のように使用します。
SELECT h.* FROM Sales.SalesOrderHeader h JOIN ShipMethod.ShipMethodID const ON h.ShipMethodID = const.[OVERNIGHT J-FAST]
またはこのように:
SELECT h.* FROM Sales.SalesOrderHeader h WHERE h.ShipMethodID = (SELECT TOP 1 [OVERNIGHT J-FAST] FROM ShipMethod.ShipMethodID)
コンスタンスが欠落している場合の私の回避策は、オプティマイザーに値に関するヒントを与えることです。
DECLARE @Constant INT = 123;
SELECT *
FROM [some_relation]
WHERE [some_attribute] = @Constant
OPTION( OPTIMIZE FOR (@Constant = 123))
これは、実行プランを作成するときに変数を定数であるかのように扱うようにクエリコンパイラに指示します。欠点は、値を2回定義する必要があることです。
いいえ。ただし、古き良き命名規則を使用する必要があります。
declare @MY_VALUE as int
FN_CONSTANT()
。そうすれば、それが何をしているのかが明確になります。
T-SQLには定数の組み込みサポートはありません。SQLMenaceのアプローチを使用してそれをシミュレートするか(ただし、他の誰かが関数を上書きして他の何かを返すかどうかはわかりませんが…)、ここで提案されているように、定数を含むテーブルを作成することもできます。おそらく、ConstantValue
列への変更をロールバックするトリガーを作成しますか?
SQL関数を使用する前に、次のスクリプトを実行して、パフォーマンスの違いを確認してください。
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'
なり... 'C201'
、code_valuesが250変数のディクショナリテーブルであり、すべてSQL-Server 2016
変数の値の最適な実行プランを取得することに関心がある場合は、動的SQLコードを使用できます。変数を一定にします。
DECLARE @var varchar(100) = 'some text'
DECLARE @sql varchar(MAX)
SET @sql = 'SELECT * FROM table WHERE col = '''+@var+''''
EXEC (@sql)
列挙型または単純な定数の場合、単一行のビューは優れたパフォーマンスを発揮し、コンパイル時のチェック/依存関係の追跡を行います(列名が原因です)。
JaredKoのブログ投稿https://blogs.msdn.microsoft.com/sql_server_appendix_z/2013/09/16/sql-server-variables-parameters-or-literals-or-constants/を参照してください。
ビューを作成する
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]
ビューを使用する
SELECT h.*
FROM Sales.SalesOrderHeader
WHERE ShipMethodID = ( select [OVERNIGHT J-FAST] from ShipMethods )
定数のサポートが組み込まれていないため、私のソリューションは非常に単純です。
これはサポートされていないため:
Declare Constant @supplement int = 240
SELECT price + @supplement
FROM what_does_it_cost
私はそれを単に変換します
SELECT price + 240/*CONSTANT:supplement*/
FROM what_does_it_cost
明らかに、これは全体(末尾のスペースとコメントのない値)が一意であることに依存しています。グローバル検索と置換で変更が可能です。
データベースの文献には「定数の作成」のようなものはありません。定数はそのまま存在し、しばしば値と呼ばれます。変数を宣言し、それに値(定数)を割り当てることができます。学問的な観点から:
DECLARE @two INT
SET @two = 2
ここで、@ twoは変数で、2は値/定数です。
2
は、「コンパイル時」に割り当てられると、バイナリ値に変換されます。エンコードされる実際の値は、割り当てられているデータ型(int、char、...)によって異なります。
スクリプト内、つまり複数のGOステートメント/バッチ間で使用する一時定数を作成する場合は、要件に応じてSQLMenaceから最良の回答が得られます。
tempdbでプロシージャを作成するだけで、ターゲットデータベースに影響を与えることはありません。
この実用的な例の1つは、論理スキーマバージョンを含むスクリプトの最後に制御値を書き込むデータベース作成スクリプトです。ファイルの上部には、変更履歴などのコメントがいくつかあります。しかし、実際には、ほとんどの開発者は、ファイルの下部にあるスキーマバージョンを下にスクロールして更新することを忘れます。
上記のコードを使用すると、データベーススクリプト(SSMSのスクリプト生成機能からコピー)がデータベースを作成する前に、表示されるスキーマバージョン定数を上部に定義できますが、最後に使用されます。これは、変更履歴やその他のコメントの横にある開発者の正面にあるため、開発者が更新する可能性が非常に高くなります。
例えば:
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
なければならない「本当の」定数(SQLにおける決定論として見られるようにUDFの要件)にこれを回します。つまり、キャッシュされてしまうはずです。それでも、+ 1。