IN operatörü (T-SQL) ile kullanılacak değişkeni tanımlama


138

IN işleci kullanan bir Transact-SQL sorgusu var. Bunun gibi bir şey:

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

"(1,2,3,4)" listesinin tamamını tutacak bir değişken tanımlamanın bir yolu var mı? Nasıl tanımlamalıyım?

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

7
Bu soru, "SQL IN yan tümcesini parametreleştirmek" sorusuyla aynı değildir. Bu soru yerel T-SQL'i, diğer soru C #'ı ifade eder.
Slogmeister Extraordinaire

Yanıtlar:


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 diyor[Err] 42000 - [SQL Server]Must declare the scalar variable "@mylist".
Cees Timmerman


5
Sadece (VALUES (1),(2),(3),(4),(5))doğrudan kullanabilir misin?
toddmo

Bu, ihtiyaçlarım için en iyi çözümdü. Değerler önceden belirlenmediği için bir Select'den aldığım Ids listesi olarak bir değişkene ihtiyacım vardı. Bu tam olarak ihtiyacım olanı başardı. Teşekkürler!
Lexi847942

12

TSQL sorguları için dinamik csv listelerini ele almanın iki yolu vardır:

1) Bir iç seçim kullanma

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

2) Dinamik olarak birleştirilmiş TSQL kullanma

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) Olası bir üçüncü seçenek tablo değişkenleridir. SQl Server 2005'iniz varsa bir tablo değişkeni kullanabilirsiniz. Sql Server 2008 üzerinde ise, tüm tablo değişkenlerini saklı yordamlara parametre olarak geçirebilir ve IN yan tümcesinde birleştirme veya alt seçim olarak kullanabilirsiniz.

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 - Bu bir genelleme ve tüm genellemeler yanlış :) Alternatifler
sundum

1
@Vilx - @list değişkenini mi ayarlamak istiyorsunuz? eğer öyleyse set gayet iyi ancak sadece bir değişken ayarlarsa, select ile bir deyimde birkaç değişken doldurabilirsiniz. Aralarında fazla bir şey olmadığı için SELECT'i kullanma alışkanlığım var.
hollystyles

1
Doğru ... çok genel. Alternatifiniz daha iyi. Gerçekten bir SQL komut dosyası içinde SQL üretmek genellikle sürdürülemez kod, enjeksiyon saldırıları riski ve diğer nastiness bir sal neden olur.
09:53

9

Bunun gibi bir işlev kullanın:

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 işlevini kullanmak yerine, işlevin döndürdüğü tabloyla bir iç birleşim yaparsınız:

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

olur

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

Dizine eklenmemiş bir 1M kayıt tablosunda ikinci sürüm yaklaşık yarısı sürdü ...

şerefe


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)

Uzun liste veya üretim sistemleri için, bu INgibi basit bir operatörden çok daha yavaş olabileceğinden someColumnName in (1,2,3,4)(8000+ öğe listesi kullanılarak test edilmiştir) bu şekilde kullanılması önerilmez.


4

Hayır, böyle bir tür yok. Ancak bazı seçenekler var:

  • Dinamik olarak oluşturulan sorgular (sp_executesql)
  • Geçici tablolar
  • Tablo tipi değişkenler (bir listede var olan en yakın şey)
  • Bir XML dizesi oluşturun ve ardından XML işlevlerine sahip bir tabloya dönüştürün (başlangıçta bir XML'niz yoksa gerçekten garip ve dolambaçlı)

Bunların hiçbiri gerçekten zarif değil, ama en iyisi bu.


4

@LukeH üzerinde küçük bir gelişme, "INTO INTO" tekrarlamaya gerek yok: ve @ realPT'nin cevabı - SELECT'e sahip olmanıza gerek yok:

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

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

4

Bunun artık eski olduğunu biliyorum ama TSQL => 2016, STRING_SPLIT kullanabilirsiniz:

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 ile başlayarak kullanabileceğiniz STRING_SPLIT ve bunu:

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

2

Bunu ikinci bir tablo kullanmadan yapmak istiyorsanız, bir CAST ile LIKE karşılaştırması yapabilirsiniz:

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

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

Karşılaştırdığınız alan zaten bir dize ise, CAST yapmanız gerekmez.

Hem sütun eşleşmesini hem de virgül içindeki her bir benzersiz değeri çevrelemek kesin bir eşleşme sağlayacaktır. Aksi takdirde, ', 4,2,15,' içeren bir listede 1 değeri bulunur.


1

Daha önce hiç kimsenin belirtmediği gibi, Sql Server 2016'dan başlayarak json dizilerini de kullanabilirsiniz 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)

Test edebilirsiniz sql fiddle demo

Ayrıca daha karmaşık durumları json ile daha kolay kapatabilirsiniz - bkz. SQL değişkeni ile WHERE IN yan tümcesini kullanarak SQL'deki değerlerin ve aralıkların arama listesi?


1

Bu, tablodaki kimlikleri sayısal olmayan sınırlandırılmış tam sayı listesine eşleştirmek için PATINDEX kullanır.

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

Notlar:

  • varchar olarak döküm yaparken ve parantez içinde bayt boyutunu belirtmezseniz varsayılan uzunluk 30'dur.
  • % (joker karakter) sıfır veya daha fazla karakter içeren herhangi bir dizeyle eşleşecek
  • ^ (joker karakter) eşleşmiyor
  • [^ 0-9] herhangi bir rakam olmayan karakterle eşleşecek
  • PATINDEX, bir dizede desenin konumunu döndüren bir SQL standart işlevidir

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;

5
orada kodunuzu açıklarsanız daha iyi bir cevap olacaktır!
Derin Kakkar

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.