Definieren Sie die Variable für den IN-Operator (T-SQL).


138

Ich habe eine Transact-SQL-Abfrage, die den IN-Operator verwendet. Etwas wie das:

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

Gibt es eine Möglichkeit, eine Variable für die gesamte Liste "(1,2,3,4)" zu definieren? Wie soll ich es definieren?

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

7
Diese Frage ist nicht mit der Frage "Parametrisieren einer SQL IN-Klausel" identisch. Diese Frage bezieht sich auf natives T-SQL, die andere Frage bezieht sich auf C #.
Slogmeister Extraordinaire

Antworten:


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

1
Behoben es für Sie @Paul
Stefan Z Camilleri

5
Können Sie einfach (VALUES (1),(2),(3),(4),(5))direkt verwenden?
Toddmo

Dies war die beste Lösung für meine Bedürfnisse. Ich brauchte eine Variable als Liste von IDs, die ich von einem Select erhielt, damit die Werte nicht vorbestimmt waren. Damit wurde genau das erreicht, was ich brauchte. Vielen Dank!
Lexi847942

12

Es gibt zwei Möglichkeiten, um dynamische CSV-Listen für TSQL-Abfragen anzugehen:

1) Verwenden einer inneren Auswahl

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

2) Verwenden von dynamisch verkettetem 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) Eine mögliche dritte Option sind Tabellenvariablen. Wenn Sie über SQl Server 2005 verfügen, können Sie eine Tabellenvariable verwenden. Wenn Sie mit SQL Server 2008 arbeiten, können Sie sogar ganze Tabellenvariablen als Parameter an gespeicherte Prozeduren übergeben und in einem Join oder als Unterauswahl in der IN-Klausel verwenden.

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 - Das ist eine Verallgemeinerung und alle Verallgemeinerungen sind falsch :) Ich habe Alternativen angeboten
Hollystyles

1
@Vilx - meinst du das Setzen der Variablen @list? Wenn dies der Fall ist, ist dies in Ordnung, setzt jedoch nur eine Variable. Mit select können Sie mehrere Variablen in einer Anweisung füllen. Da es nicht viel zwischen ihnen gibt, habe ich die Angewohnheit, immer SELECT zu verwenden.
Hollystyles

1
Stimmt ... sehr allgemein. Ihre Alternative ist besser. Ich meine wirklich, dass das Generieren von SQL aus einem SQL-Skript normalerweise nicht wartbaren Code, das Risiko von Injektionsangriffen und eine Reihe anderer Gemeinheiten verursacht.
Badbod99

9

Verwenden Sie eine Funktion wie diese:

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;

Anstatt like zu verwenden, erstellen Sie einen inneren Join mit der von der Funktion zurückgegebenen Tabelle:

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

wird

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

In einer nicht indizierten 1M-Datensatztabelle dauerte die zweite Version ungefähr die Hälfte der Zeit ...

Prost


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)

Bitte beachten Sie, dass es für lange Listen oder Produktionssysteme nicht empfohlen wird, diese Methode zu verwenden, da sie möglicherweise viel langsamer ist als einfache INBediener someColumnName in (1,2,3,4)(getestet mit einer Liste mit mehr als 8000 Artikeln).


4

Nein, es gibt keinen solchen Typ. Es gibt jedoch einige Möglichkeiten:

  • Dynamisch generierte Abfragen (sp_executesql)
  • Temporäre Tische
  • Variablen vom Tabellentyp (am nächsten an einer Liste)
  • Erstellen Sie eine XML-Zeichenfolge und konvertieren Sie sie dann in eine Tabelle mit den XML-Funktionen (sehr umständlich und umständlich, es sei denn, Sie haben zunächst eine XML-Datei).

Keines davon ist wirklich elegant, aber das ist das Beste, was es gibt.


4

leichte Verbesserung gegenüber @LukeH, es besteht keine Notwendigkeit, das "INSERT INTO" zu wiederholen: und die Antwort von @ realPT - es ist nicht erforderlich, das SELECT zu haben:

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

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

4

Ich weiß, dass dies jetzt alt ist, aber TSQL => 2016 können Sie STRING_SPLIT verwenden:

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

Ab SQL2017 können Sie STRING_SPLIT verwenden und dies tun:

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

2

Wenn Sie dies ohne Verwendung einer zweiten Tabelle tun möchten, können Sie einen LIKE-Vergleich mit einem CAST durchführen:

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

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

Wenn das Feld, das Sie vergleichen, bereits eine Zeichenfolge ist, müssen Sie CAST nicht ausführen.

Wenn Sie sowohl die Spaltenübereinstimmung als auch jeden eindeutigen Wert in Kommas umgeben, wird eine genaue Übereinstimmung sichergestellt. Andernfalls würde ein Wert von 1 in einer Liste mit ', 4,2,15,' gefunden.



1

Dieser verwendet PATINDEX, um IDs aus einer Tabelle mit einer nicht durch Ziffern getrennten Ganzzahlliste abzugleichen.

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

Anmerkungen:

  • Beim Casting als varchar und ohne Angabe der Bytegröße in Klammern beträgt die Standardlänge 30
  • % (Platzhalter) stimmt mit jeder Zeichenfolge mit null oder mehr Zeichen überein
  • ^ (Platzhalter) nicht übereinstimmen
  • [^ 0-9] stimmt mit jedem nichtstelligen Zeichen überein
  • PATINDEX ist eine SQL-Standardfunktion, die die Position eines Musters in einer Zeichenfolge zurückgibt

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
Es ist eine bessere Antwort, wenn Sie Ihren Code dort beschreiben!
Deep Kakkar

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.