Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Passer une variable dans une clause IN dans une fonction SQL ?

Voici un moyen un peu plus efficace de diviser une liste d'entiers. Tout d'abord, créez une table de nombres, si vous n'en avez pas déjà une. Cela créera une table avec 100 000 entiers uniques (vous aurez peut-être besoin de plus ou de moins) :

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

Puis une fonction :

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

Vous pouvez comparer les performances à une approche itérative ici :

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

Pour éviter le tableau des nombres, vous pouvez également essayer une version XML de la fonction - elle est plus compacte mais moins efficace :

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

Quoi qu'il en soit, une fois que vous avez une fonction, vous pouvez simplement dire :

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