2009-10-06 17 views
14

Quiero escribir un procedimiento almacenado de SQL Server 2005 que seleccionará y devolverá los registros del usuario de la tabla de usuario para algunos ID de usuario que se pasan al procedimiento almacenado como parámetro.SQL: cláusula in en procedimiento almacenado: cómo pasar valores

¿Cómo hacer esto?

Puedo pasar los identificadores de usuario como una cadena separada por comas. Para que pueda usar el

select * 
from users 
where userid in (userids) 

Por ej. : Quiero seleccionar registros para id's 5,6,7,8,9

¿Cómo se escribe el procedimiento almacenado?

+0

Esta es una pregunta común. Tengo una opción aquí: http://stackoverflow.com/questions/977021/can-a-stored-procedure-have-dynamic-parameters-to-be-used-in-an-in-clause/977114#977114 – ScottE

Respuesta

17

Para SQL Server 2005, echa un vistazo a el excelente artículo de Erland Sommarskog Arrays and Lists in SQL Server 2005 que muestra algunas técnicas de cómo hacer tratar con listas y matrices en SQL Server 2005 (también tiene otro artículo para SQL Server 2000).

Si se pudiera actualizar a SQL Server 2008, puede utilizar la nueva característica llamada "parámetro con valores de tabla":

En primer lugar, crear una definida por el usuario tipo de tabla

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

En segundo lugar, el uso que se tipo de tabla en el procedimiento almacenado como un parámetro:

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

Ver detalles here.

Marc

7

puede usar sql dinámico. Pase el de declaración a un SQL SP a través de una variable y concatenar en una consulta en el SQL y ejecutar utilizando sp_execute sql

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

funciona, pero el SQL dinámico tiene su cuota de problemas (seguridad, rendimiento, etc.) –

3

Suponiendo T-SQL, puede utilizar esta función agradable (que devuelve una tabla).

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 esta manera:

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

Usted puede cambiar la función almacenada para trabajar con cadenas separadas por comas en lugar de los separadas por espacios.

Si no desea/no puede usar una función almacenada, puede incluir el código dentro del procedimiento almacenado donde sea necesario.

EDITAR: esto es increíblemente más eficaz que la concatenación de cadenas.

+0

¿Tiene estadísticas al respecto? Me interesaría leerlos –

+0

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

+0

Woah! ¡Gracias KM, haciendo un par de pruebas y reemplazándolo para proyectar ahora! De todos modos, mi "esto es más rápido" se refería a sql dinámico. –

4

ver my previous answer to this

esta es la mejor fuente:

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

crear una función de división, y usarlo como:

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

I prefer the number table approach

Por esta metho d funcione, es necesario realizar esta configuración de la tabla una vez:

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 vez que los números de la tabla está configurado, crear esta función:

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 

Ahora puede fácilmente dividir una cadena CSV en una mesa y unirse en él:

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

SALIDA:

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

(6 row(s) affected) 

Tu puede pasar una serie de CSV en un procedimiento de proceso y sólo las filas de los ID dados:

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

Admito que he estado usando esto desde hace varios años, pero me volvió a morder el culo. La versión fragmentada de varias afirmaciones de esto (aquí: http://www.sommarskog.se/arrays-in-sql-2005.html#tblnum) es dos veces más rápida. El problema solo se notó cuando la tabla involucrada contenía cerca de un millón de filas. Así que tenlo en cuenta antes de utilizar la solución de @ KM. – pkExec

8

sólo lo utilizan como esto va a funcionar

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

Gracias por su consejo compañero! Solo ** exec ** funcionó para mí en mi procedimiento 'insert en #temp_hourly exec (' seleccione ZONE_ID de ZONE donde ZONE_ID en ('+ @ZoneId +') grupo por ZONE_ID ') ' –

-1

También puede utilizar FIND_IN_SET en lugar de EN . Ver la consulta a continuación

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' no es un SQL función estándar, y está ** no ** disponible en Microsoft ** SQL Server ** (que el OP estaba preguntando) –

0

intente esto funciona para mí

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