Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Passare una variabile in una clausola IN all'interno di una funzione SQL?

Ecco un modo leggermente più efficiente per dividere un elenco di numeri interi. Per prima cosa, crea una tabella di numeri, se non ne hai già una. Questo creerà una tabella con 100.000 interi univoci (potresti aver bisogno di più o meno):

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

Poi una funzione:

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

Puoi confrontare le prestazioni con un approccio iterativo qui:

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

Per evitare la tabella dei numeri, puoi anche provare una versione della funzione basata su XML:è più compatta ma meno efficiente:

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

Comunque una volta che hai una funzione puoi semplicemente dire:

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