TSQL変数を定数にする方法はありますか?


回答:


60

いいえ。ただし、関数を作成してそこにハードコーディングして使用することはできます。

次に例を示します。

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

SELECT dbo.fnConstant()

13
WITH SCHEMABINDING なければならない「本当の」定数(SQLにおける決定論として見られるようにUDFの要件)にこれを回します。つまり、キャッシュされてしまうはずです。それでも、+ 1。
ジョナサンディキンソン

この答えは良いです。sqlserverのテーブル列がデフォルト値として関数を参照できるかどうかだけが気になります。私はこれを機能させることができませんでした
Ab Bennett

1
@JonathanDickinsonが明確であるために、あなたの提案が使用するWITH SCHEMABINDINGにはCREATE FUNCTION(関数を呼び出すことがありますストアドプロシージャではなく)声明-その権利がありますか?
ホリスティック開発者

1
はい、関数内です。WITH SCHEMABINDINGを使用すると、SQLで「インラインテーブル値関数」をインライン化できます。したがって、gist.github.com / jcdickinson / 61a38dedb84b35251da301b128535cebの形式にする必要もあります。クエリアナライザは、SCHEMABINDINGまたはBEGINを使用しないと何もインライン化しません。
ジョナサン・ディキンソン

非決定性のUDFを使用する意味は:docs.microsoft.com/es-es/archive/blogs/sqlprogrammability/...
Ochoto

28

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)

1
これは、受け入れられている答えよりもはるかに優れたソリューションです。最初にスカラー関数ルートをたどりましたが、パフォーマンスがひどいです。この回答と、上記のJaredKoの記事へのリンクの方がはるかに優れています。
David Coster 2018

ただし、WITH SCHEMABINDINGをスカラー関数に追加すると、パフォーマンスが大幅に向上するようです。
David Coster 2018

リンクは現在無効です。
MatthieuCormier19年

1
@MatthieuCormier:リンクを更新しましたが、MSDNが古いURLから新しいURLへのリダイレクトを追加したようです。
IlmariKaronen19年

23

コンスタンスが欠落している場合の私の回避策は、オプティマイザーに値に関するヒントを与えることです。

DECLARE @Constant INT = 123;

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

これは、実行プランを作成するときに変数を定数であるかのように扱うようにクエリコンパイラに指示します。欠点は、値を2回定義する必要があることです。


3
それは役に立ちますが、単一の定義の目的も無効にします。
MikeJRamsey56 2018

9

いいえ。ただし、古き良き命名規則を使用する必要があります。

declare @MY_VALUE as int

@VictorYarema必要なのは、慣例だけだからです。そして時々あなたは他に良い選択がないからです。さて、それはさておき、SQLMenaceの答えは良く見えます、私はあなたに同意します。それでも、関数名は定数IMOの規則に従う必要があります。名前を付ける必要がありますFN_CONSTANT()。そうすれば、それが何をしているのかが明確になります。
tfrascaroli 2016年

パフォーマンスを向上させたい場合は、これだけでは役に立ちません。MichalD。とJohnNilssonの回答を試して、パフォーマンスを向上させてください。
WonderWorker

8

T-SQLには定数の組み込みサポートはありません。SQLMenaceのアプローチを使用してそれをシミュレートするか(ただし、他の誰かが関数を上書きして他の何かを返すかどうかはわかりませんが…)、ここで提案されているように、定数を含むテーブルを作成することもできます。おそらく、ConstantValue列への変更をロールバックするトリガーを作成しますか?


7

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

4
これはかなり古いですが、参考までに、サーバーで実行したときの結果は次のとおりです。2760ms elapsed, using function| 2300ms elapsed, using local variable| 2286ms elapsed, using hard coded values|
z00l 2015年

2
開発用ラップトップで、スキーマバインディングなしの2つの追加関数。5570 elapsed, using function | 406 elapsed, using local variable| 383 elapsed, using hard coded values| 3893 elapsed, using function without schemabinding
モンキーハウス2017

比較のために、単純なselectステートメントは4110msかかりました。ここでは、selectステートメントが交互にselect top 1 @m = cv_val from code_values where cv_id = 'C101' なり... 'C201' 、code_valuesが250変数のディクショナリテーブルであり、すべてSQL-Server 2016
Monkeyhouse 2017

6

変数の値の最適な実行プランを取得することに関心がある場合は、動的SQLコードを使用できます。変数を一定にします。

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

1
これが私のやり方であり、定数を含むクエリのパフォーマンスが大幅に向上します。
WonderWorker 2017年

5

列挙型または単純な定数の場合、単一行のビューは優れたパフォーマンスを発揮し、コンパイル時のチェック/依存関係の追跡を行います(列名が原因です)。

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  )

3

さて、見てみましょう

定数は不変の値であり、コンパイル時に認識され、プログラムの存続期間中は変更されません。

つまり、SQLServerで定数を持つことはできません。

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

値が変更されました


1

定数のサポートが組み込まれていないため、私のソリューションは非常に単純です。

これはサポートされていないため:

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

私はそれを単に変換します

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

明らかに、これは全体(末尾のスペースとコメントのない値)が一意であることに依存しています。グローバル検索と置換で変更が可能です。


1つの問題は、ローカルでしか利用できないことです
Bernardo DalCorno19年

0

データベースの文献には「定数の作成」のようなものはありません。定数はそのまま存在し、しばしば値と呼ばれます。変数を宣言し、それに値(定数)を割​​り当てることができます。学問的な観点から:

DECLARE @two INT
SET @two = 2

ここで、@ twoは変数で、2は値/定数です。


MichalD。とJohnNilssonの回答を試して、パフォーマンスを向上させてください。
WonderWorker 2017年

リテラルは定義上一定です。ascii / unicode(エディターによって異なります)文字2は、「コンパイル時」に割り当てられると、バイナリ値に変換されます。エンコードされる実際の値は、割り当てられているデータ型(int、char、...)によって異なります。
samis 2018

-1

スクリプト内、つまり複数の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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.