sql >> Base de Datos >  >> RDS >> Sqlserver

¿Pasar una variable a una cláusula IN dentro de una función SQL?

Aquí hay una forma un poco más eficiente de dividir una lista de números enteros. Primero, cree una tabla de números, si aún no tiene una. Esto creará una tabla con 100 000 enteros únicos (es posible que necesite más o menos):

;WITH x AS
(
   SELECT TOP (1000000) Number = ROW_NUMBER() OVER 
   (ORDER BY s1.[object_id])
   FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
   ORDER BY s1.[object_id]
)
SELECT Number INTO dbo.Numbers FROM x;

CREATE UNIQUE CLUSTERED INDEX n ON dbo.Numbers(Number);

Entonces una función:

CREATE FUNCTION [dbo].[SplitInts_Numbers]
(
   @List       NVARCHAR(MAX),
   @Delimiter  NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN
   (
       SELECT Item = CONVERT(INT, SUBSTRING(@List, Number,
         CHARINDEX(@Delimiter, @List + @Delimiter, Number) - Number))
       FROM dbo.Numbers
       WHERE Number <= CONVERT(INT, LEN(@List))
         AND SUBSTRING(@Delimiter + @List, Number, 1) = @Delimiter
   );

Puede comparar el rendimiento con un enfoque iterativo aquí:

http://sqlfiddle.com/#!3/960d2/1

Para evitar la tabla de números, también puede probar una versión de la función basada en XML; es más compacta pero menos eficiente:

CREATE FUNCTION [dbo].[SplitInts_XML]
(
   @List       VARCHAR(MAX),
   @Delimiter  CHAR(1)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN ( SELECT Item = CONVERT(INT, Item) FROM ( 
     SELECT Item = x.i.value('(./text())[1]', 'int') FROM ( 
       SELECT [XML] = CONVERT(XML, '<i>' + REPLACE(@List, @Delimiter, '</i><i>') 
       + '</i>').query('.') ) AS a CROSS APPLY [XML].nodes('i') AS x(i)) AS y
     WHERE Item IS NOT NULL
   );

De todos modos, una vez que tenga una función, simplemente puede decir:

WHERE ID IN (SELECT Item FROM dbo.SplitInts_Numbers(@MyList, ','));