web-dev-qa-db-fra.com

Définir la variable à utiliser avec l'opérateur IN (T-SQL)

J'ai une requête Transact-SQL qui utilise l'opérateur IN. Quelque chose comme ça:

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

Y at-il un moyen de définir une variable pour contenir la liste complète "(1,2,3,4)"? Comment devrais-je le définir?

declare @myList {data type}
set @myList = (1,2,3,4)
select * from myTable where myColumn in @myList
101
Marcos Crispino
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)
92
LukeH
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)
37
realPT

Utilisez une fonction comme celle-ci:

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;

Au lieu d'utiliser like, vous créez une jointure interne avec la table renvoyée par la fonction:

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

devient

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

Dans une table d'enregistrement 1M non indexée, la seconde version prenait environ la moitié du temps ...

à votre santé

8
allaphor

Il existe deux manières de traiter les listes de fichiers csv dynamiques pour les requêtes TSQL:

1) Utiliser un select interne

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

2) Utilisation de TSQL concaténé dynamiquement

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) Une troisième option possible est les variables de table. Si vous avez SQL Server 2005, vous pouvez utiliser une variable de table. Si vous utilisez SQL Server 2008, vous pouvez même transmettre des variables de table entières en tant que paramètre aux procédures stockées et les utiliser dans une jointure ou comme sous-sélection dans la clause 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)
6
Holly Styles
DECLARE @myList TABLE (Id BIGINT) INSERT INTO @myList(Id) VALUES (1),(2),(3),(4);
select * from myTable where myColumn in(select Id from @myList)

Veuillez noter que pour les systèmes de longue liste ou de production, il est déconseillé d'utiliser cette méthode, car elle peut être beaucoup plus lente que INoperator comme someColumnName in (1,2,3,4) (testé avec une liste de plus de 8 000 éléments)

5
Vova

Non, il n'y a pas un tel type. Mais il y a quelques choix:

  • Requêtes générées dynamiquement (sp_executesql)
  • Tables temporaires
  • Variables de type table (élément le plus proche d'une liste)
  • Créez une chaîne XML, puis convertissez-la en une table avec les fonctions XML (vraiment gênant et rond-point, sauf si vous avez un XML pour commencer)

Aucune d'entre elles n'est vraiment élégante, mais c'est la meilleure qui soit.

3
Vilx-

légère amélioration sur @LukeH, il n'y a pas besoin de répéter les réponses "INSERT INTO": et @ realPT - pas besoin d'avoir le SELECT:

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

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

Je sais que c'est vieux maintenant mais TSQL => 2016, vous pouvez utiliser 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)
2
Nathan Evans

À partir de SQL2017, vous pouvez utiliser STRING_SPLIT et procédez comme suit:

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

Si vous voulez faire cela sans utiliser une seconde table, vous pouvez faire une comparaison avec LIKE avec un CAST:

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

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

Si le champ que vous comparez est déjà une chaîne, vous n'avez pas besoin de CAST.

Entourer à la fois la correspondance de colonne et chaque valeur unique entre virgules assurera une correspondance exacte. Sinon, une valeur de 1 serait trouvée dans une liste contenant ', 4,2,15,'

2
Michael Reyes

Comme personne ne l’a mentionné auparavant, à partir de Sql Server 2016, vous pouvez également utiliser des tableaux json et 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)

Vous pouvez le tester dans sql fiddle demo

Vous pouvez également couvrir plus facilement des cas plus complexes avec JSON - voir Liste de recherche de valeurs et plage en SQL à l'aide de la clause WHERE IN avec une variable SQL?

0
Roman Pekar

Je pense que vous devrez déclarer une chaîne, puis exécuter cette chaîne SQL.

Regardez sp_ExecuteSQL

0
BIDeveloper
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;