Definir variable para usar con el operador IN (T-SQL)

138

Tengo una consulta Transact-SQL que usa el operador IN. Algo como esto:

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

¿Hay alguna manera de definir una variable para contener toda la lista "(1,2,3,4)"? ¿Cómo debo definirlo?

declare @myList {data type}
set @myList = (1,2,3,4)
select * from myTable where myColumn in @myList
Marcos Crispino
fuente
77
Esta pregunta no es lo mismo que la pregunta "Parametrizar una cláusula SQL IN". Esta pregunta se refiere a T-SQL nativo, la otra pregunta se refiere a C #.
Slogmeister Extraordinaire

Respuestas:

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)
LukeH
fuente
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)
realPT
fuente
T-SQL dice[Err] 42000 - [SQL Server]Must declare the scalar variable "@mylist".
Cees Timmerman
1
Lo arregló para ti @Paul
Stefan Z Camilleri
55
¿Puedes usar (VALUES (1),(2),(3),(4),(5))directamente?
toddmo
Esta fue la mejor solución para mis necesidades. Necesitaba una variable como una lista de ID que estaba obteniendo de un Select para que los valores no estuvieran predeterminados. Esto logró exactamente lo que necesitaba. ¡Gracias!
Lexi847942
12

Hay dos formas de abordar las listas de csv dinámicas para consultas TSQL:

1) Usando una selección interna

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

2) Uso de TSQL concatenado dinámicamente

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) Una posible tercera opción son las variables de tabla. Si tiene SQl Server 2005, puede usar una variable de tabla. Si está en Sql Server 2008, incluso puede pasar variables de tabla completas como un parámetro a los procedimientos almacenados y usarlo en una combinación o como una subselección en la cláusula 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
fuente
55
@ badbod99 - Eso es una generalización y todas las generalizaciones están mal :) He ofrecido alternativas
hollystyles
1
@Vilx: ¿quieres decir para configurar la variable @list? si está establecido, está bien pero solo establece una variable, con select puede completar varias variables en una declaración. Como no hay mucho entre ellos, suelo usar siempre SELECT.
hollystyles
1
Cierto ... muy general. Tu alternativa es mejor. Realmente quiero decir que generar SQL desde una secuencia de comandos SQL generalmente causa código que no se puede mantener, riesgo de ataques de inyección y una serie de otras cosas desagradables.
badbod99
9

Use una función como esta:

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;

En lugar de usar like, realiza una unión interna con la tabla devuelta por la función:

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

se convierte

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

En una tabla de registro 1M no indexada, la segunda versión tardó aproximadamente la mitad del tiempo ...

salud

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

Tenga en cuenta que para una lista larga o sistemas de producción no se recomienda usar de esta manera, ya que puede ser mucho más lento que el simple INoperador someColumnName in (1,2,3,4)(probado con una lista de más de 8000 artículos)

Vova
fuente
4

No, no existe tal tipo. Pero hay algunas opciones:

  • Consultas generadas dinámicamente (sp_executesql)
  • Mesas temporales
  • Variables de tipo tabla (lo más parecido a una lista)
  • Cree una cadena XML y luego conviértala en una tabla con las funciones XML (realmente incómodo y rotundo, a menos que tenga un XML para comenzar)

Ninguno de estos es realmente elegante, pero eso es lo mejor que hay.

Vilx-
fuente
4

ligera mejora en @LukeH, no hay necesidad de repetir "INSERT INTO": y la respuesta de @ realPT - no es necesario tener SELECT:

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

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

fuente
4

Sé que esto es antiguo ahora, pero TSQL => 2016, puede usar 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
fuente
4

A partir de SQL2017, puede usar STRING_SPLIT y hacer esto:

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

Si desea hacer esto sin usar una segunda tabla, puede hacer una comparación LIKE con un CAST:

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

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

Si el campo que está comparando ya es una cadena, no necesitará CAST.

Rodeando tanto la coincidencia de columna como cada valor único en comas asegurará una coincidencia exacta. De lo contrario, un valor de 1 se encontraría en una lista que contiene ', 4,2,15,'

Michael Reyes
fuente
1

Éste usa PATINDEX para hacer coincidir los identificadores de una tabla con una lista de enteros delimitados sin dígitos.

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

Notas:

  • cuando se convierte como varchar y no se especifica el tamaño del byte entre paréntesis, la longitud predeterminada es 30
  • % (comodín) coincidirá con cualquier cadena de cero o más caracteres
  • ^ (comodín) no coincide
  • [^ 0-9] coincidirá con cualquier carácter que no sea un dígito
  • PATINDEX es una función estándar de SQL que devuelve la posición de un patrón en una cadena
Marne
fuente
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
fuente
55
¡será una mejor respuesta si describe su código allí!
Deep Kakkar
0

Creo que tendrá que declarar una cadena y luego ejecutar esa cadena SQL.

Echa un vistazo a sp_executeSQL

BIDeveloper
fuente