IN演算子で使用する変数を定義(T-SQL)


138

IN演算子を使用するTransact-SQLクエリがあります。このようなもの:

select * from myTable where myColumn in (1,2,3,4)

リスト「(1,2,3,4)」全体を保持する変数を定義する方法はありますか?どのように定義すればよいですか?

declare @myList {data type}
set @myList = (1,2,3,4)
select * from myTable where myColumn in @myList

7
この質問は、「SQL IN句のパラメーター化」の質問と同じではありません。この質問はネイティブT-SQLを指し、他の質問はC#を指します。
Slogmeister Extraordinaire、2015

回答:


113
DECLARE @MyList TABLE (Value INT)
INSERT INTO @MyList VALUES (1)
INSERT INTO @MyList VALUES (2)
INSERT INTO @MyList VALUES (3)
INSERT INTO @MyList VALUES (4)

SELECT *
FROM MyTable
WHERE MyColumn IN (SELECT Value FROM @MyList)

47
DECLARE @mylist TABLE (Id int)
INSERT INTO @mylist
SELECT id FROM (VALUES (1),(2),(3),(4),(5)) AS tbl(id)

SELECT * FROM Mytable WHERE theColumn IN (select id from @mylist)

T-SQLの[Err] 42000 - [SQL Server]Must declare the scalar variable "@mylist".
発言

1
あなたのためにそれを修正しました@Paul
Stefan Z Camilleri

5
(VALUES (1),(2),(3),(4),(5))直接使用できますか?
toddmo 2016年

これは私のニーズに最適なソリューションでした。値が事前に決定されないように、Selectから取得するIDのリストとして変数が必要でした。これは私が必要とするものを正確に達成しました。ありがとう!
Lexi847942 2017

12

TSQLクエリの動的csvリストに取り組む方法は2つあります。

1)内部選択の使用

SELECT * FROM myTable WHERE myColumn in (SELECT id FROM myIdTable WHERE id > 10)

2)動的に連結されたTSQLの使用

DECLARE @sql varchar(max)  
declare @list varchar(256)  
select @list = '1,2,3'  
SELECT @sql = 'SELECT * FROM myTable WHERE myColumn in (' + @list + ')'

exec sp_executeSQL @sql

3)可能な3番目のオプションはテーブル変数です。SQl Server 2005を使用している場合は、テーブル変数を使用できます。SQL Server 2008の場合、テーブル変数全体をパラメータとしてストアドプロシージャに渡し、結合で使用したり、IN句の副選択として使用したりすることもできます。

DECLARE @list TABLE (Id INT)

INSERT INTO @list(Id)
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4


SELECT
    * 
FROM 
    myTable
    JOIN @list l ON myTable.myColumn = l.Id

SELECT
    * 
FROM 
    myTable
WHERE
    myColumn IN (SELECT Id FROM @list)

5
@ badbod99-それは一般化であり、すべての一般化は間違っています:)私は代替案を提供しています
hollystyles 2009年

1
@Vilx-変数@listを設定することを意味しますか?そのように設定した場合は問題ありませんが、設定する変数は1つだけです。selectを使用すると、1つのステートメントに複数の変数を設定できます。それらの間にはほとんどないので、私は常にSELECTを使用する癖があります。
hollystyles 2009年

1
確かに…非常に一般的です。あなたの代わりは良いです。SQLスクリプト内からSQLを生成すると、通常、保守不能なコード、インジェクション攻撃のリスク、およびその他の多くの厄介な問題が発生します。
badbod99 2009年

9

次のような関数を使用します。

CREATE function [dbo].[list_to_table] (@list varchar(4000))
returns @tab table (item varchar(100))
begin

if CHARINDEX(',',@list) = 0 or CHARINDEX(',',@list) is null
begin
    insert into @tab (item) values (@list);
    return;
end


declare @c_pos int;
declare @n_pos int;
declare @l_pos int;

set @c_pos = 0;
set @n_pos = CHARINDEX(',',@list,@c_pos);

while @n_pos > 0
begin
    insert into @tab (item) values (SUBSTRING(@list,@c_pos+1,@n_pos - @c_pos-1));
    set @c_pos = @n_pos;
    set @l_pos = @n_pos;
    set @n_pos = CHARINDEX(',',@list,@c_pos+1);
end;

insert into @tab (item) values (SUBSTRING(@list,@l_pos+1,4000));

return;
end;

likeを使用する代わりに、関数によって返されたテーブルとの内部結合を作成します。

select * from table_1 where id in ('a','b','c')

なる

select * from table_1 a inner join [dbo].[list_to_table] ('a,b,c') b on (a.id = b.item)

インデックス付けされていない1Mレコードテーブルでは、2番目のバージョンの所要時間は約半分でした...

乾杯


5
DECLARE @myList TABLE (Id BIGINT) INSERT INTO @myList(Id) VALUES (1),(2),(3),(4);
select * from myTable where myColumn in(select Id from @myList)

長いリストまたは本番システムでは、この方法を使用することはお勧めしません。これは、単純なIN演算子someColumnName in (1,2,3,4)(8000以上の項目リストを使用してテスト済み)のようなものよりもはるかに遅いためです。


4

いいえ、そのようなタイプはありません。しかし、いくつかの選択肢があります。

  • 動的に生成されたクエリ(sp_executesql)
  • 一時テーブル
  • テーブル型変数(リストに最も近いもの)
  • XML文字列を作成し、それをXML関数を使用してテーブルに変換します(最初にXMLがない限り、実際には扱いにくく、ラウンドアバウトです)。

これらはどれも本当にエレガントではありませんが、それが最高です。


4

@LukeHのわずかな改善、 "INSERT INTO"を繰り返す必要はありません:@realPTの答え-SELECTを持つ必要はありません:

DECLARE @MyList TABLE (Value INT) 
INSERT INTO @MyList VALUES (1),(2),(3),(4)

SELECT * FROM MyTable
WHERE MyColumn IN (SELECT Value FROM @MyList)

4

私はこれが古いことを知っていますが、TSQL => 2016、STRING_SPLITを使用できます:

DECLARE @InList varchar(255) = 'This;Is;My;List';

WITH InList (Item) AS (
    SELECT value FROM STRING_SPLIT(@InList, ';')
)

SELECT * 
FROM [Table]
WHERE [Item] IN (SELECT Tag FROM InList)

4

SQL2017以降で使用できるSTRING_SPLITをして次の操作を行います。

declare @myList nvarchar(MAX)
set @myList = '1,2,3,4'
select * from myTable where myColumn in (select value from STRING_SPLIT(@myList,','))

2

2番目のテーブルを使用せずにこれを行う場合は、CASTを使用してLIKE比較を行うことができます。

DECLARE @myList varchar(15)
SET @myList = ',1,2,3,4,'

SELECT *
FROM myTable
WHERE @myList LIKE '%,' + CAST(myColumn AS varchar(15)) + ',%'

比較するフィールドが既に文字列の場合、CASTする必要はありません。

列の一致と各一意の値の両方をコンマで囲むと、完全に一致します。そうでない場合、値「1」は、「、4,2,15、」を含むリストで見つかります。


1

以前に誰も言及しなかったように、Sql Server 2016からは、json配列やOPENJSON (Transact-SQL)次のものも使用できます。

declare @filter nvarchar(max) = '[1,2]'

select *
from dbo.Test as t
where
    exists (select * from openjson(@filter) as tt where tt.[value] = t.id)

あなたはそれをテストすることができます sql fiddle demo

jsonでより複雑なケースを簡単にカバーすることもできます。SQL変数でWHERE IN句を使用してSQLの値と範囲のリストを検索するをご覧ください


1

これは、PATINDEXを使用して、テーブルのIDを数字で区切られていない整数リストと照合します。

-- Given a string @myList containing character delimited integers 
-- (supports any non digit delimiter)
DECLARE @myList VARCHAR(MAX) = '1,2,3,4,42'

SELECT * FROM [MyTable]
    WHERE 
        -- When the Id is at the leftmost position 
        -- (nothing to its left and anything to its right after a non digit char) 
        PATINDEX(CAST([Id] AS VARCHAR)+'[^0-9]%', @myList)>0 
        OR
        -- When the Id is at the rightmost position
        -- (anything to its left before a non digit char and nothing to its right) 
        PATINDEX('%[^0-9]'+CAST([Id] AS VARCHAR), @myList)>0
        OR
        -- When the Id is between two delimiters 
        -- (anything to its left and right after two non digit chars)
        PATINDEX('%[^0-9]'+CAST([Id] AS VARCHAR)+'[^0-9]%', @myList)>0
        OR
        -- When the Id is equal to the list
        -- (if there is only one Id in the list)
        CAST([Id] AS VARCHAR)=@myList

ノート:

  • varcharとしてキャストし、括弧内にバイトサイズを指定しない場合、デフォルトの長さは30です。
  • %(ワイルドカード)は0個以上の文字の任意の文字列に一致します
  • ^(ワイルドカード)一致しない
  • [^ 0-9]は数字以外の文字に一致します
  • PATINDEXは、文字列内のパターンの位置を返すSQL標準関数です


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