Tentukan variabel yang akan digunakan dengan operator IN (T-SQL)

141

Saya memiliki kueri Transact-SQL yang menggunakan operator IN. Sesuatu seperti ini:

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

Apakah ada cara untuk mendefinisikan variabel untuk menampung seluruh daftar "(1,2,3,4)"? Bagaimana saya harus mendefinisikannya?

declare @myList {data type}
set @myList = (1,2,3,4)
select * from myTable where myColumn in @myList
Marcos Crispino
sumber
7
Pertanyaan ini tidak sama dengan pertanyaan "Parameterize an SQL IN clause". Pertanyaan ini mengacu pada native T-SQL, pertanyaan lainnya mengacu pada C #.
Slogmeister Extraordinaire

Jawaban:

115
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)
LukeH
sumber
48
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)
realPT
sumber
T-SQL mengatakan[Err] 42000 - [SQL Server]Must declare the scalar variable "@mylist".
Cees Timmerman
1
Memperbaiki untuk Anda @Paul
Stefan Z Camilleri
7
Bisakah Anda (VALUES (1),(2),(3),(4),(5))langsung menggunakan ?
toddmo
Ini adalah solusi terbaik untuk kebutuhan saya. Saya membutuhkan variabel sebagai daftar Id yang saya dapatkan dari Select sehingga nilainya tidak ditentukan sebelumnya. Ini mencapai apa yang saya butuhkan. Terima kasih!
Lexi847942
13

Ada dua cara untuk menangani daftar csv dinamis untuk kueri TSQL:

1) Menggunakan seleksi dalam

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

2) Menggunakan TSQL yang digabungkan secara dinamis

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) Kemungkinan opsi ketiga adalah variabel tabel. Jika Anda memiliki SQl Server 2005 Anda dapat menggunakan variabel tabel. Jika Anda di Sql Server 2008, Anda bahkan dapat mengirimkan seluruh variabel tabel sebagai parameter untuk prosedur yang tersimpan dan menggunakannya dalam gabungan atau sebagai subpilih dalam klausa 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)
hollystyles
sumber
5
@ badbod99 - Itu generalisasi dan semua generalisasi salah :) Saya telah menawarkan alternatif
hollystyles
1
@Vilx - maksud Anda untuk menyetel variabel @list? jika begitu set baik-baik saja tetapi hanya menetapkan satu variabel, dengan memilih Anda dapat mengisi beberapa variabel dalam satu pernyataan. Karena tidak banyak di antara mereka, saya memiliki kebiasaan untuk selalu menggunakan SELECT.
hollystyles
1
Benar ... sangat umum. Alternatif Anda lebih baik. Maksud saya menghasilkan SQL dari dalam skrip SQL biasanya menyebabkan kode yang tidak dapat dipelihara, risiko serangan injeksi dan rakit kejahatan lainnya.
badbod99
9

Gunakan fungsi seperti ini:

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;

Alih-alih menggunakan like, Anda membuat gabungan dalam dengan tabel yang dikembalikan oleh fungsi:

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

menjadi

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

Dalam tabel catatan 1 juta yang tidak diindeks, versi kedua membutuhkan waktu sekitar separuh waktu ...

Bersulang

allaphor
sumber
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)

Harap dicatat bahwa untuk daftar panjang atau sistem produksi tidak disarankan menggunakan cara ini karena mungkin jauh lebih lambat daripada INoperator sederhana seperti someColumnName in (1,2,3,4)(diuji menggunakan 8000+ daftar item)

Volodymyr
sumber
4

Tidak, tidak ada tipe seperti itu. Tetapi ada beberapa pilihan:

  • Kueri yang dibuat secara dinamis (sp_executesql)
  • Tabel sementara
  • Variabel tipe tabel (hal terdekat yang ada pada daftar)
  • Buat string XML dan kemudian konversikan menjadi tabel dengan fungsi XML (benar-benar canggung dan berputar-putar, kecuali Anda memiliki XML untuk memulai)

Tak satu pun dari ini benar-benar elegan, tapi itulah yang terbaik.

Vilx-
sumber
4

sedikit perbaikan pada @LukeH, tidak perlu mengulang "INSERT INTO": dan jawaban @ realPT - tidak perlu memiliki SELECT:

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

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

sumber
4

Saya tahu ini sudah tua sekarang tetapi TSQL => 2016, Anda dapat menggunakan 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)
Nathan Evans
sumber
4

Dimulai dengan SQL2017, Anda dapat menggunakan STRING_SPLIT dan melakukan ini:

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

Jika Anda ingin melakukan ini tanpa menggunakan tabel kedua, Anda dapat melakukan perbandingan LIKE dengan CAST:

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

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

Jika bidang yang Anda bandingkan sudah berupa string, Anda tidak perlu melakukan CAST.

Mengitari kecocokan kolom dan setiap nilai unik dalam koma akan memastikan kecocokan persis. Jika tidak, nilai 1 akan ditemukan dalam daftar yang berisi ', 4,2,15,'

Michael Reyes
sumber
1

Karena tidak ada yang menyebutkannya sebelumnya, mulai dari Sql Server 2016 Anda juga dapat menggunakan json array dan 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)

Anda dapat mengujinya di sql fiddle demo

Anda juga dapat mencakup kasus yang lebih rumit dengan json lebih mudah - lihat Cari daftar nilai dan rentang dalam SQL menggunakan klausa WHERE IN dengan variabel SQL?

Roman Pekar
sumber
1

Yang ini menggunakan PATINDEX untuk mencocokkan id dari tabel ke daftar bilangan bulat yang dipisahkan bukan digit.

-- 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

Catatan:

  • saat mentransmisikan sebagai varchar dan tidak menentukan ukuran byte dalam tanda kurung, panjang default adalah 30
  • % (wildcard) akan cocok dengan sembarang string dari nol atau lebih karakter
  • ^ (karakter pengganti) tidak cocok
  • [^ 0-9] akan cocok dengan semua karakter bukan digit
  • PATINDEX adalah fungsi standar SQL yang mengembalikan posisi pola dalam string
Marne
sumber
0
DECLARE @StatusList varchar(MAX);
SET @StatusList='1,2,3,4';
DECLARE @Status SYS_INTEGERS;
INSERT INTO  @Status 
SELECT Value 
FROM dbo.SYS_SPLITTOINTEGERS_FN(@StatusList, ',');
SELECT Value From @Status;
Muhammed Fatih Yıldız
sumber
5
itu akan menjadi jawaban yang lebih baik jika Anda menjelaskan kode Anda di sana!
Deep Kakkar
0

Saya pikir Anda harus mendeklarasikan string dan kemudian mengeksekusi string SQL itu.

Lihat sp_executeSQL

BIDeveloper
sumber