Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Passando uma variável para uma cláusula IN dentro de uma função SQL?


Aqui está uma maneira um pouco mais eficiente de dividir uma lista de inteiros. Primeiro, crie uma tabela de números, se você ainda não tiver uma. Isso criará uma tabela com 100.000 inteiros únicos (você pode precisar de mais ou 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);

Então uma função:
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
   );

Você pode comparar o desempenho com uma abordagem iterativa aqui:

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

Para evitar a tabela de números, você também pode tentar uma versão da função baseada em XML - é mais compacta, mas 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 qualquer forma, uma vez que você tenha uma função, você pode simplesmente dizer:
WHERE ID IN (SELECT Item FROM dbo.SplitInts_Numbers(@MyList, ','));