整数のリストを保持するSQL変数


175

他の誰かのSQLレポートをデバッグしようとしていますが、基になるレポートクエリをSQL 2012のクエリウィンドウに配置しました。

レポートが要求するパラメータの1つは、整数のリストです。これは、レポートで複数選択ドロップダウンボックスを使用して実現されます。レポートの基になるクエリでは、この整数リストをwhere句で使用します。

select *
from TabA
where TabA.ID in (@listOfIDs)

デバッグしているクエリを変更したくありませんが、SQL Serverでこのタイプのデータを保持してテストできる変数を作成する方法がわかりません。

例えば

declare @listOfIDs int
set listOfIDs  = 1,2,3,4

整数のリストを保持できるデータ型はないので、SQL Serverでレポートと同じ値を使用してレポートクエリを実行するにはどうすればよいですか?


1
TVPテーブル値パラメーターを使用してデータを挿入したことはわかっていますが、どこで使用できるか確認しました。続編?
パパラッツォ

2
よく書かれた質問。+1
RayLoveless 2017年

回答:


224

テーブル変数

declare @listOfIDs table (id int);
insert @listOfIDs(id) values(1),(2),(3);    

select *
from TabA
where TabA.ID in (select id from @listOfIDs)

または

declare @listOfIDs varchar(1000);
SET @listOfIDs = ',1,2,3,'; --in this solution need put coma on begin and end

select *
from TabA
where charindex(',' + CAST(TabA.ID as nvarchar(20)) + ',', @listOfIDs) > 0

2
ありがとうございます。ただし、クエリで変数が読み取られる方法を書き換える必要があります。私はそれを同じに保つ必要があります。
ErickTreetops 2013

3
IDが何であるかわからず、クエリから取得された場合はどうなりますか?例:SET @AddressIDs = (SELECT ID FROM address WHERE Account = 1234)このクエリは複数のIDを返し、サブクエリが複数の結果を返し、それは許可されていないというエラーが表示されます。とにかく、サブクエリからのIDの場合に配列を格納する変数を作成する方法はありますか?
ラファエルモレイラ2016年

1
2番目のオプションを試したところ、レコード数が少なくなります。IDの数を増やすと、TimeOutエラーが発生します。キャストがパフォーマンスを妨げているのかもしれません。
ユーザーM

元の質問に対する答えではありませんが、私が尋ねなかった質問に対する答えなので、私は元気です。List <int>をパラメーターとして渡しますが、SSMSでテストするためのローカル変数を作成します。それはキラーです。
ウェイドハトラー、2018

すばらしい回答で、多くの時間を節約できました
Alfredo A.

35

変数が次のようなものであると仮定します。

CREATE TYPE [dbo].[IntList] AS TABLE(
[Value] [int] NOT NULL
)

ストアドプロシージャは次の形式で使用しています。

ALTER Procedure [dbo].[GetFooByIds]
    @Ids [IntList] ReadOnly
As 

IntListを作成して、次のようにプロシージャを呼び出すことができます。

Declare @IDs IntList;
Insert Into @IDs Select Id From dbo.{TableThatHasIds}
Where Id In (111, 222, 333, 444)
Exec [dbo].[GetFooByIds] @IDs

または、IntListを自分で提供する場合

DECLARE @listOfIDs dbo.IntList
INSERT INTO @listofIDs VALUES (1),(35),(118);

17

そうです、SQL-Serverには整数のリストを保持できるデータ型がありません。しかし、あなたができることは、整数のリストを文字列として保存することです。

DECLARE @listOfIDs varchar(8000);
SET @listOfIDs = '1,2,3,4';

次に、文字列を個別の整数値に分割して、それらをテーブルに入れることができます。あなたの手順はすでにこれをしているかもしれません。

動的クエリを使用して同じ結果を達成することもできます。

DECLARE @SQL nvarchar(8000);

SET @SQL = 'SELECT * FROM TabA WHERE TabA.ID IN (' + @listOfIDs + ')';
EXECUTE (@SQL);

ありがとう、でも私が許可されていないクエリを変更する必要があります。
ErickTreetops 2013

2
誰かがこれを使用する場合、@ listOfIDsがユーザーによって提供される文字列パラメータである場合、SQLインジェクションに対して非常に脆弱になる可能性があることに注意してください。アプリのアーキテクチャによっては、これが問題になる場合と問題にならない場合があります。
Rogala 2017年

@Rogala同意する、ユーザーは必要に応じて独自の衛生管理を行う必要があります。
Möoz

1
@Möozこれを反映するために、回答にメモを追加することをお勧めします。誰もが知っているわけではなく、結果を考えずにソリューションをコピーして貼り付けるだけです。動的SQLは非常に危険であり、私はその疫病のようにそれを避けます。
Rogala

@Möozまた、私はあなたの回答に反対票を投じませんでしたが、数分かけて私の回答をチェックしてください。STRING_SPLIT関数はかなり甘いです、そしてあなたはそれですべてになると思います!!
Rogala 2017年

6

SQL Server 2016+とAzure SQL Databaseの場合、この問題の完全な解決策となるSTRING_SPLIT関数が追加されました。ここにドキュメントがあります:https : //docs.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql

次に例を示します。

/*List of ids in a comma delimited string
  Note: the ') WAITFOR DELAY ''00:00:02''' is a way to verify that your script 
        doesn't allow for SQL injection*/
DECLARE @listOfIds VARCHAR(MAX) = '1,3,a,10.1,) WAITFOR DELAY ''00:00:02''';

--Make sure the temp table was dropped before trying to create it
IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL DROP TABLE #MyTable;

--Create example reference table
CREATE TABLE #MyTable
([Id] INT NOT NULL);

--Populate the reference table
DECLARE @i INT = 1;
WHILE(@i <= 10)
BEGIN
    INSERT INTO #MyTable
    SELECT @i;

    SET @i = @i + 1;
END

/*Find all the values
  Note: I silently ignore the values that are not integers*/
SELECT t.[Id]
FROM #MyTable as t
    INNER JOIN 
        (SELECT value as [Id] 
        FROM STRING_SPLIT(@listOfIds, ',')
        WHERE ISNUMERIC(value) = 1 /*Make sure it is numeric*/
            AND ROUND(value,0) = value /*Make sure it is an integer*/) as ids
    ON t.[Id] = ids.[Id];

--Clean-up
DROP TABLE #MyTable;

クエリの結果は1,3です

〜乾杯


4

最後に、クエリの動作を変更しないと、変数に値を格納できないという結論に達しました。SQLプロファイラーを使用して値をキャッチし、それをクエリにハードコーディングして、どのように機能するかを確認しました。これらの整数配列は18個あり、一部には30個を超える要素が含まれていました。

MS / SQLがいくつかの追加のデータ型を言語に導入する必要があると思います。配列は非常に一般的であり、ストアドプロシージャで配列を使用できなかった理由はわかりません。


6
SQL Serverには、テーブル値のパラメーターと変数がある場合、配列は必要ありません。
John Saunders

1
つまり、クエリは整数のリストを使用していることを知っています(配列によって渡されますか?)。私が理解していないのは、回答で指定されたメソッドの1つを利用せずに、クエリがそれらをどのように使用していたかです。さらにコンテキストを提供していただければ、さらにお手伝いします。
Möoz

2

このようにすることはできませんが、クエリ全体を実行して変数に格納できます。

例えば:

DECLARE @listOfIDs NVARCHAR(MAX) = 
    '1,2,3'

DECLARE @query NVARCHAR(MAX) = 
    'Select *
     From TabA
     Where TabA.ID in (' + @listOfIDs + ')'

Exec (@query)

2
前のコメントで述べたように、このタイプのソリューションの実装方法によっては、@ listOfIDsがユーザーによって提供されたパラメーターである場合、SQLインジェクションに対して脆弱になる可能性があることに注意してください。
Rogala 2017年

2

string_split文字列のリストを使用している場合に呼び出されるSQLの新しい関数があります。参照リンクSTRING_SPLIT(Transact-SQL)

DECLARE @tags NVARCHAR(400) = 'clothing,road,,touring,bike'
SELECT value
FROM STRING_SPLIT(@tags, ',')
WHERE RTRIM(value) <> '';

in次のようにしてこのクエリを渡すことができます:

SELECT *
  FROM [dbo].[yourTable]
  WHERE (strval IN (SELECT value FROM STRING_SPLIT(@tags, ',') WHERE RTRIM(value) <> ''))

1
もっともらしいようですが、残念ながらSQL Server 2016以降のみです。
AnotherFineMess

0

私はこれを使います:

1-建物のスクリプトで一時テーブル変数を宣言します。

DECLARE @ShiftPeriodList TABLE(id INT NOT NULL);

2-一時テーブルに割り当てる:

IF (SOME CONDITION) 
BEGIN 
        INSERT INTO @ShiftPeriodList SELECT ShiftId FROM [hr].[tbl_WorkShift]
END
IF (SOME CONDITION2)
BEGIN
    INSERT INTO @ShiftPeriodList
        SELECT  ws.ShiftId
        FROM [hr].[tbl_WorkShift] ws
        WHERE ws.WorkShift = 'Weekend(VSD)' OR ws.WorkShift = 'Weekend(SDL)'

END

3-WHEREステートメントで必要なときにテーブルを参照します。

INSERT INTO SomeTable WHERE ShiftPeriod IN (SELECT * FROM @ShiftPeriodList)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.