2009-10-06 6 views
14

Je souhaite écrire une procédure stockée SQL Server 2005 qui sélectionnera et renverra les enregistrements d'utilisateur de la table utilisateur pour certains ID utilisateur transmis à la procédure stockée en tant que paramètre.SQL: in clause dans la procédure stockée: comment transmettre des valeurs

Comment faire?

Je peux transmettre les ID utilisateur sous la forme d'une chaîne séparée par une virgule. Pour que je puisse utiliser le

select * 
from users 
where userid in (userids) 

E.g. : Je veux sélectionner les enregistrements pour les ID 5,6,7,8,9

Comment écrire la procédure stockée?

+0

Ceci est une question commune. J'ai une option ici: http://stackoverflow.com/questions/977021/can-a-stored-procedure-have-dynamic-parameters-to-be-used-in-an-in-clause/977114#977114 – ScottE

Répondre

17

Pour SQL Server 2005, consultez excellent article Arrays and Lists in SQL Server 2005 de Erland Sommarskog qui montre quelques techniques comment à gérer les listes et les tableaux dans SQL Server 2005 (il dispose également d'un autre article pour SQL Server 2000).

Si vous pouvez mettre à niveau vers SQL Server 2008, vous pouvez utiliser la nouvelle fonctionnalité appelée "table de paramètre valeur":

d'abord, créez un type de table défini par l'utilisateur

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

En second lieu, l'utilisation que type de table dans votre procédure stockée en tant que paramètre:

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

Voir détails here.

Marc

7

vous pouvez utiliser SQL dynamique. Faire passer le dans l'instruction à un Sql SP via une variable et concaténer dans une requête dans le SQL et exécuter à l'aide sp_execute sql

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

fonctionne, mais SQL dynamique a sa part de problèmes (sécurité, performance, etc.) –

3

En supposant T-SQL, vous pouvez utiliser cette belle fonction (qui retourne une table).

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 

De cette façon:

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

Vous pouvez modifier la fonction stockée pour travailler avec des chaînes séparées par des virgules au lieu de ceux séparés par des espaces.

Si vous ne voulez pas/ne pouvez pas utiliser une fonction stockée, vous pouvez inclure le code dans la procédure stockée si nécessaire.

EDIT: c'est incroyablement plus performant que la concaténation de chaîne.

+0

Avez-vous des statistiques à ce sujet? Je serais intéressé à les lire –

+0

c'est lent, vérifiez cela: http://stackoverflow.com/questions/1456192/comparing-a-column-to-a-list-of-values-in-t-sql/1456404 # 1456404 –

+0

Woah! Merci KM, faire quelques tests et le remplacer pour projeter maintenant! Quoi qu'il en soit, mon "c'est plus rapide" signifiait se référer à dynamique SQL. –

4

voir my previous answer to this

c'est la meilleure source:

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

créer une fonction split, et l'utiliser comme:

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

I prefer the number table approach

Pour cette métho d à travailler, vous devez faire une configuration horaire:

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) 

Une fois la table des numéros est mis en place, créez cette fonction:

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 

Vous pouvez maintenant facilement découper une chaîne CSV dans un table et rejoindre là-dessus:

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

SORTIE:

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

(6 row(s) affected) 

Votre peut passer dans une chaîne CSV dans une seule procédure et traiter les lignes pour les ID donnés:

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

J'avoue que je l'utilise depuis plusieurs années maintenant, mais il est revenu me mordre dans le cul. La version de multi-déclaration de chunking de ceci (ici: http://www.sommarskog.se/arrays-in-sql-2005.html#tblnum) est en effet deux fois plus rapide. Le problème n'était perceptible que lorsque la table impliquée contenait près d'un million de lignes. Donc, prenez cela en considération avant d'utiliser la solution @ KM. – pkExec

8

Il suffit de l'utiliser comme ceci fonctionnera

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

Merci pour le conseil mate! Just ** exec ** a fonctionné pour moi dans ma procédure 'insert dans #temp_hourly exec ('select ZONE_ID de ZONE où ZONE_ID dans (' + @ZoneId + ') groupe par ZONE_ID')' –

-1

Vous pouvez également utiliser FIND_IN_SET au lieu de . Voir ci-dessous la requête

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' n'est pas un SQL fonction standard, et c'est ** pas ** disponible dans Microsoft ** SQL Server ** (que l'OP demandait à propos de) –

0

essayer ce que cela fonctionne pour moi

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