2009-10-06 18 views
14

Voglio scrivere una stored procedure di SQL Server 2005 che selezionerà e restituirà i record utente dalla tabella utente per alcuni userids che vengono passati alla stored procedure come parametro.SQL: nella clausola nella stored procedure: come passare i valori

Come fare questo?

Posso passare gli ID utente come una stringa separata da una virgola. In modo che io possa utilizzare il

select * 
from users 
where userid in (userids) 

E.g. : Voglio selezionare i record per ID 5,6,7,8,9

Come scrivere la stored procedure?

+0

Questa è una domanda comune. Ho un'opzione qui: http://stackoverflow.com/questions/977021/can-a-stored-procedure-have-dynamic-parameters-to-be-used-in-an-in-clause/977114#977114 – ScottE

risposta

17

Per SQL Server 2005, controllare eccellente Arrays and Lists in SQL Server 2005 articolo di Erland Sommarskog che mostra alcune tecniche Come fare per trattare con liste e array in SQL Server 2005 (ha anche un altro articolo per SQL Server 2000).

Se si potesse eseguire l'aggiornamento a SQL Server 2008, è possibile utilizzare la nuova funzionalità chiamata "con valori di tabella dei parametri":

In primo luogo, creare un tipo definito dall'utente tavolo

CREATE TYPE dbo.MyUserIDs AS TABLE (UserID INT NOT NULL) 

In secondo luogo, l'uso che tipo di tabella nella stored procedure come parametro:

CREATE PROC proc_GetUsers @UserIDTable MyUserIDs READONLY 
AS 
SELECT * FROM dbo.Users 
    WHERE userid IN (SELECT UserID FROM @UserIDTable) 

Vedere dettagli here.

Marc

7

potresti usare dynamic sql. Passare la in dichiarazione a uno Sql SP tramite una variabile e concatenare in una query in SQL ed eseguire utilizzando sp_execute SQL

create procedure myproc(@clause varchar(100)) as 
begin 
    exec sp_executesql 'select * from users where userid in (' + @clause +')' 
end 
+8

funziona, ma SQL dinamico ha la sua parte di problemi (sicurezza, prestazioni, ecc.) –

3

Supponendo T-SQL, è possibile utilizzare questa bella funzione (che restituisce una tabella).

DROP FUNCTION sp_ConvertStringToTable 
GO 
CREATE FUNCTION sp_ConvertStringToTable(@list ntext) 
     RETURNS @tbl TABLE (Position INT IDENTITY(1, 1) NOT NULL, 
          Value INT NOT NULL) AS 
    BEGIN 
     DECLARE @pos  int, 
       @textpos int, 
       @chunklen smallint, 
       @str  nvarchar(4000), 
       @tmpstr nvarchar(4000), 
       @leftover nvarchar(4000) 

     SET @textpos = 1 
     SET @leftover = '' 
     WHILE @textpos <= datalength(@list)/2 
     BEGIN 
     SET @chunklen = 4000 - datalength(@leftover)/2 
     SET @tmpstr = ltrim(@leftover + substring(@list, @textpos, @chunklen)) 
     SET @textpos = @textpos + @chunklen 

     SET @pos = charindex(' ', @tmpstr) 
     WHILE @pos > 0 
     BEGIN 
      SET @str = substring(@tmpstr, 1, @pos - 1) 
      INSERT @tbl (Value) VALUES(convert(int, @str)) 
      SET @tmpstr = ltrim(substring(@tmpstr, @pos + 1, len(@tmpstr))) 
      SET @pos = charindex(' ', @tmpstr) 
     END 

     SET @leftover = @tmpstr 
     END 

     IF ltrim(rtrim(@leftover)) <> '' 
     INSERT @tbl (Value) VALUES(convert(int, @leftover)) 

     RETURN 
    END 
GO 

In questo modo:

SELECT * FROM Users 
WHERE userid IN 
(SELECT Value FROM sp_ConvertStringToTable('1 2 3')) 

È possibile modificare la funzione memorizzata di lavorare con separati da virgola corde invece di quelli spaziali separate.

Se non si desidera/non è possibile utilizzare una funzione memorizzata, è possibile includere il codice all'interno della stored procedure, laddove necessario.

EDIT: questo è incredibilmente più performante rispetto alla concatenazione di stringhe.

+0

Hai statistiche a riguardo? Sarei interessato a leggerli –

+0

questo è lento, guarda questo: http://stackoverflow.com/questions/1456192/comparing-a-column-to-a-list-of-values-in-t-sql/1456404 # 1456404 –

+0

Woah! Grazie KM, facendo un paio di test e sostituendolo per proiettare ora! In ogni caso, il mio "questo è più veloce" era inteso facendo riferimento a sql dinamico. –

4

vedere my previous answer to this

questa è la migliore fonte:

http://www.sommarskog.se/arrays-in-sql.html

creare una funzione split, e utilizzarlo come:

SELECT 
    * 
    FROM YourTable y 
    INNER JOIN dbo.splitFunction(@Parameter) s ON y.ID=s.Value 

I prefer the number table approach

Per questo meto d al lavoro, è necessario fare questa configurazione tavolo una volta:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number 
    INTO Numbers 
    FROM sys.objects s1 
    CROSS JOIN sys.objects s2 
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number) 

Una volta che i numeri della tabella viene istituito, a creare questa funzione:

CREATE FUNCTION [dbo].[FN_ListToTable] 
(
    @SplitOn char(1)  --REQUIRED, the character to split the @List string on 
    ,@List  varchar(8000)--REQUIRED, the list to split apart 
) 
RETURNS TABLE 
AS 
RETURN 
(

    ---------------- 
    --SINGLE QUERY-- --this will not return empty rows 
    ---------------- 
    SELECT 
     ListValue 
     FROM (SELECT 
        LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue 
        FROM (
          SELECT @SplitOn + @List + @SplitOn AS List2 
         ) AS dt 
         INNER JOIN Numbers n ON n.Number < LEN(dt.List2) 
        WHERE SUBSTRING(List2, number, 1) = @SplitOn 
      ) dt2 
     WHERE ListValue IS NOT NULL AND ListValue!='' 

); 
GO 

Si può ora facilmente dividere una stringa CSV in un tavolo e di unirsi a esso:

select * from dbo.FN_ListToTable(',','1,2,3,,,4,5,6777,,,') 

USCITA:

ListValue 
----------------------- 
1 
2 
3 
4 
5 
6777 

(6 row(s) affected) 

Il tuo può passare in una stringa CSV in una procedura e di processo solo le righe per gli ID indicati:

SELECT 
    y.* 
    FROM YourTable y 
     INNER JOIN dbo.FN_ListToTable(',',@GivenCSV) s ON y.ID=s.ListValue 
+0

Ammetto che lo sto usando da diversi anni, ma è tornato a mordermi nel culo. La versione multi-statement chunking di questo (qui: http://www.sommarskog.se/arrays-in-sql-2005.html#tblnum) è infatti due volte più veloce. Il problema era evidente solo quando la tabella coinvolta conteneva quasi un milione di righe. Prendilo quindi in considerazione prima di utilizzare la soluzione di @ KM. – pkExec

8

semplicemente utilizzarlo come questo funzionerà

Create procedure sp_DoctorList 
@userid varchar(100) 
as 
begin 
exec ('select * from doctor where userid in ('+ @userid +')') 
end 
+0

Grazie per il consiglio amico! Solo ** exec ** parte ha funzionato per me nella mia procedura 'inserisci in #temp_hourly exec ('seleziona ZONE_ID da ZONE dove ZONE_ID in (' + @ZoneId + ') raggruppa per ZONE_ID')' –

-1

È anche possibile utilizzare Find_IN_SET invece che in . Vedere la domanda sotto

create procedure myproc(IN in_user_ids varchar(100)) 
begin 
    select * from users where FIND_IN_SET(userid, in_user_ids); 
end 
+0

'FIND_IN_SET' non è un SQL funzione standard, ed è ** non ** disponibile in Microsoft ** SQL Server ** (che l'OP stava chiedendo) –

0

provare questo questo funziona per me

DECLARE @InClause NVARCHAR(100) 
SET @InClause = 'tom,dick,harry' 
DECLARE @SafeInClause NVARCHAR(100) 
SET @SafeInClause = ',' + @InClause + ',' 
SELECT * FROM myTable WHERE PATINDEX(',' + myColumn + ',', @SafeInClause) > 0 
Problemi correlati