2011-01-06 29 views
5

Estoy desarrollando una aplicación ASP.NET y pasando un valor de cadena como "1,2,3,4" en un procedimiento para seleccionar los valores que están IN (1,2, 3,4) pero dice "La conversión falló al convertir el valor varchar '1,2,3,4' al tipo de datos int".SQL usa valores separados por comas con la cláusula IN

Este es el código aspx:

private void fillRoles() 
{ 
    /*Read in User Profile Data from database */ 
    Database db = DatabaseFactory.CreateDatabase(); 

    DbCommand cmd = db.GetStoredProcCommand("sp_getUserRoles"); 

    db.AddInParameter(cmd, "@pGroupIDs", System.Data.DbType.String); 
    db.SetParameterValue(cmd, "@pGroupIDs", "1,2,3,4"); 

    IDataReader reader = db.ExecuteReader(cmd); 

    DropDownListRole.DataTextField = "Group"; 
    DropDownListRole.DataValueField = "ID"; 

    while (reader.Read()) 
    { 
     DropDownListRole.Items.Add((new ListItem(reader[1].ToString(), reader[0].ToString()))); 
    } 

    reader.Close(); 
} 

Aquí es mi procedimiento:

CREATE Procedure [dbo].[sp_getUserRoles](@pGroupIDs varchar(50)) 
AS BEGIN 
    SELECT * FROM CheckList_Groups Where id in (@pGroupIDs) 
END 

Respuesta

8

Aquí es una workaround que encontré para hacer lo que usted está tratando de lograr

CREATE Procedure [dbo].[sp_getUserRoles](
    @pGroupIDs varchar(50) 
    ) 
    As 
    BEGIN 
     SELECT * FROM CheckList_Groups Where (',' + @pGroupIDs +',' LIKE '%,' + CONVERT(VARCHAR, id) + ',%') 
    End 

Esto le da su lista delimitada por comas y lo compara con los identificadores (que se representan como tal ',1,', ',2,' etc.) en la tabla usando LIKE

+0

Fantasticoooooooooo :) :) Drahcir funcionó como encanto – user342944

+2

No creo que esta solución se comportará bien en grandes conjuntos de datos, ya que no se pueden usar índices en este caso. (Los índices no se pueden usar cuando se prefija el argumento de búsqueda de una expresión similar con un comodín). –

1

Es necesario utilizar Sp_executesql para lograr este functionllity

CREATE Procedure [dbo].[sp_getUserRoles](
    @pGroupIDs varchar(50) 
    ) 
    As 
    BEGIN 

EXECUTE sp_executesql 
      N'SELECT * FROM CheckList_Groups Where id in (@pGroupIDs)', 
      N'@level varchar(50)', 
      @level = @pGroupIDs; 

End 
+0

significas construir una cadena SQL y ejecutar? – user342944

+0

@ user342944 - sí –

1

La cláusula IN puede' t toma un parámetro vinculado como ese. Lo que se está dando cuando la consulta es realmente creada es SELECT * FROM CheckList_Groups Where id in ('1,2,3,4'). Esencialmente, la cláusula IN se pasa una sola cadena.

6

Claro que no puede hacer eso,

La consulta generada sería algo como esto

SELECT * FROM CheckList_Groups Where id in ('1,2,3,4') 

y asegurarse de que no se puede ejecutar.

se puede construir la consulta en el procedimiento almacenado luego ejecutarlo con exec

'SELECT * FROM CheckList_Groups Where id in (' + @pGroupIDs + ')' 

o

SELECT * FROM CheckList_Groups Where charindex(','+id+',' , @pGroupIDs)>0 

pero primero hay que añadir el ',' al inicio y final de su parámetro en C# código

3

No es posible poner esos valores (la secuencia separada por comas) en un valor de parámetro.

Lo que tendrá que hacer es crear dinámicamente la instrucción SQL en el procedimiento almacenado, mediante la concatenación de cadenas. Tendrás que ejecutarlo con el procedimiento almacenado sp_executesql y luego.

CREATE PROCEDURE [dbo].[getUserRoles](@groupIds NVARCHAR(50)) 
AS BEGIN 
    DECLARE @statement NVARCHAR(255) 

    SELECT @statement = N'SELECT * FROM CheckList_Groups Where id in (' + @pGroupIDs + N')'  

    execute sp_executesql @statement 
END 

Además, no es que le puse al SP getUserRoles en lugar de sp_getUserRoles. La razón es muy simple: cuando ejecuta un procedimiento almacenado cuyo nombre comienza con sp_, SQL Server consultará primero la base de datos master para encontrar ese procedimiento almacenado, lo que provoca un golpe de rendimiento en curso.

+0

Gracias Fredrik por esta valiosa sugerencia, lo tendría en cuenta con certeza :) – user342944

+0

inyección gallore –

8

Si no desea utilizar SQL dinámico, el mejor que he de manera encontrado es crear una función que convierte una cadena delimitada en una tabla, algo como esto trabaja para una lista de números enteros:

CREATE FUNCTION [dbo].[StringToIntList] 
(@str VARCHAR (MAX), @delimeter CHAR (1)) 
RETURNS 
    @result TABLE (
     [ID] INT NULL) 
AS 
BEGIN 

    DECLARE @x XML 
    SET @x = '<t>' + REPLACE(@str, @delimeter, '</t><t>') + '</t>' 

    INSERT INTO @result 
    SELECT DISTINCT x.i.value('.', 'int') AS token 
    FROM @x.nodes('//t') x(i) 
    ORDER BY 1 

RETURN 
END 

A continuación, utilice que en su sp:

CREATE Procedure [dbo].[sp_getUserRoles](
    @pGroupIDs varchar(50) 
    ) 
    As 
    BEGIN 
     SELECT * FROM CheckList_Groups Where id in (
      SELECT ID FROM dbo.StringToIntList(@pGroupIds,',') 
     ) 
    End 
+0

gracias por el truco – 0cool

+0

un poco lento, pero funciona. – johnny

+0

bien en realidad no es lento. MI consulta ya era lenta – johnny

3

La forma en que está tratando de hacer esto es un poco incorrecta. Deberá usar EXECUTE para lograr esto.

CREATE PROCEDURE [dbo].[sp_getUserRoles](@pGroupIDs nvarchar(50)) 
As 
BEGIN   
    EXECUTE (N'SELECT * FROM CheckList_Groups Where id in (' + @pGroupIDs + ')'; 
END 
+0

Ardman Obtengo 'El procedimiento espera el parámetro' @statement 'del tipo' ntext/nchar/nvarchar 'error cuando intenté urs. ahora voy a intentar con otros – user342944

+0

¿Eso es después de mi edición? –

+0

El error dice que debe usar NVARCHAR. Entonces, hágalo así: EJECUTAR (N'SELECT ... 'y especifique que el tipo del parámetro pGroupdIDS es también una NVARCHAR. –

0

Primera función crear -

Sólo tiene que ejecutar este código

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE FUNCTION [dbo].[CSVToTable] (@InStr VARCHAR(MAX)) 
RETURNS @TempTab TABLE 
    (id int not null) 
AS 
BEGIN 
    ;-- Ensure input ends with comma 
    SET @InStr = REPLACE(@InStr + ',', ',,', ',') 
    DECLARE @SP INT 
DECLARE @VALUE VARCHAR(1000) 
WHILE PATINDEX('%,%', @INSTR) <> 0 
BEGIN 
    SELECT @SP = PATINDEX('%,%',@INSTR) 
    SELECT @VALUE = LEFT(@INSTR , @SP - 1) 
    SELECT @INSTR = STUFF(@INSTR, 1, @SP, '') 
    INSERT INTO @TempTab(id) VALUES (@VALUE) 
END 
    RETURN 
END 
GO 

Entonces -

Función utilice el soporte con selecto DECLARACIÓN -

DECLARE @LIST VARCHAR(200) 
SET @LIST = '1,3' 
SELECT Id, Descr FROM CSVDemo WHERE Id IN (SELECT * FROM dbo.CSVToTable(@LIST)) 
Cuestiones relacionadas