2010-05-31 5 views
30

Disons que je suis arrivé ce qui suit:SQL Server - Dans la clause avec une variable déclarée

DECLARE @ExcludedList VARCHAR(MAX) 

SET @ExcludedList = 3 + ', ' + 4 + ' ,' + '22' 

SELECT * FROM A WHERE Id NOT IN (@ExcludedList) 

Erreur: Échec de la conversion de la valeur varchar '' le type de données int.

Je comprends pourquoi l'erreur est là, mais je ne sais pas comment le résoudre ...

+0

@TomTom et al - Je suis en désaccord que c'est un doublon. L'autre question couvre plus de terrain non lié à ce que cette question aborde dans le spécifique. Plus important encore, je suis content d'avoir trouvé ce post et pas l'autre - car celui-ci a résolu mon problème avec précision. – condiosluzverde

Répondre

30

Vous devez l'exécuter comme un sp dynamique comme

DECLARE @ExcludedList VARCHAR(MAX) 

SET @ExcludedList = '3,4,22,6014' 
declare @sql nvarchar(Max) 

Set @sql='SELECT * FROM [A] WHERE Id NOT IN ('[email protected]+')' 

exec sp_executesql @sql 
+7

N'est-ce pas vulnérable à l'injection SQL si ExcludedList est entré par un utilisateur? Par exemple, ExcludedList = '3); UTILISATEURS DE TABLE DE DÉPÔT; - '(le système de commentaire ne me laisse pas utiliser (à) signe) – ristonj

+0

Peu importe. Plusieurs variables (une par élément) ou SQL dynamique sont les seules façons de gérer cela. Oui, il faut faire attention. Ou supprimez la clause in (et chargez les éléments dans une variable table/table temporaire, puis rejoignez). – TomTom

+0

Je préférerais utiliser un séparateur de chaînes au lieu de SQL dynamique pour éviter l'injection SQL. http://sqlperformance.com/2012/07/t-sql-queries/split-strings –

0

Je pense que problème est dans

3 + ', ' + 4 

changement à

'3' + ', ' + '4' 

DECLARE @ExcludedList VARCHAR(MAX) 

SET @ExcludedList = '3' + ', ' + '4' + ' ,' + '22' 

SELECT * FROM A WHERE Id NOT IN (@ExcludedList) 

SET @ExcludedListe telle que votre requête devienne

soit

SELECT * FROM A WHERE Id NOT IN ('3', '4', '22') 

ou

SELECT * FROM A WHERE Id NOT IN (3, 4, 22) 
+1

Cela corrige l'instruction set, mais la requête select ne fonctionnera toujours pas –

+0

Cela a fonctionné pour moi brillamment –

0

Essayez ceci:

CREATE PROCEDURE MyProc @excludedlist integer_list_tbltype READONLY AS 
    SELECT * FROM A WHERE ID NOT IN (@excludedlist) 

Et puis appelez comme ceci:

DECLARE @ExcludedList integer_list_tbltype 
INSERT @ExcludedList(n) VALUES(3, 4, 22) 
exec MyProc @ExcludedList 
1

Vous ne pouvez pas utiliser une variable un IN clause - vous devez utiliser dynamic SQL ou utiliser a function (TSQL or CLR) to convert the list of values into a table.

exemple SQL dynamique:

DECLARE @ExcludedList VARCHAR(MAX) 
    SET @ExcludedList = 3 + ',' + 4 + ',' + '22' 

DECLARE @SQL NVARCHAR(4000) 
    SET @SQL = 'SELECT * FROM A WHERE Id NOT IN (@ExcludedList) ' 

BEGIN 

    EXEC sp_executesql @SQL '@ExcludedList VARCHAR(MAX)' @ExcludedList 

END 
16
DECLARE @IDQuery VARCHAR(MAX) 
SET @IDQuery = 'SELECT ID FROM SomeTable WHERE Condition=Something' 
DECLARE @ExcludedList TABLE(ID VARCHAR(MAX)) 
INSERT INTO @ExcludedList EXEC(@IDQuery)  
SELECT * FROM A WHERE Id NOT IN (@ExcludedList) 

Je sais que je réponds à un ancien poste, mais je voulais partager un exemple d'utilisation des tables de variables lorsque l'on veut éviter d'utiliser le SQL dynamique. Je ne suis pas sûr que ce soit le moyen le plus efficace, mais cela a fonctionné dans le passé pour moi quand le SQL dynamique n'était pas une option.

+3

J'aime ça parce que vous avez évité d'utiliser le SQL dynamique – MattK311

3

D'abord, créer une fonction rapide qui va diviser une liste délimitée par des valeurs dans une table, comme ceci:

CREATE FUNCTION dbo.udf_SplitVariable 
(
    @List varchar(8000), 
    @SplitOn varchar(5) = ',' 
) 

RETURNS @RtnValue TABLE 
(
    Id INT IDENTITY(1,1), 
    Value VARCHAR(8000) 
) 

AS 
BEGIN 

--Account for ticks 
SET @List = (REPLACE(@List, '''', '')) 

--Account for 'emptynull' 
IF LTRIM(RTRIM(@List)) = 'emptynull' 
BEGIN 
    SET @List = '' 
END 

--Loop through all of the items in the string and add records for each item 
WHILE (CHARINDEX(@SplitOn,@List)>0) 
BEGIN 

    INSERT INTO @RtnValue (value) 
    SELECT Value = LTRIM(RTRIM(SUBSTRING(@List, 1, CHARINDEX(@SplitOn, @List)-1))) 

    SET @List = SUBSTRING(@List, CHARINDEX(@SplitOn,@List) + LEN(@SplitOn), LEN(@List)) 

END 

INSERT INTO @RtnValue (Value) 
SELECT Value = LTRIM(RTRIM(@List)) 

RETURN 

END 

appellent ensuite le fonction comme ça ...

SELECT * 
FROM A 
LEFT OUTER JOIN udf_SplitVariable(@ExcludedList, ',') f ON A.Id = f.Value 
WHERE f.Id IS NULL 

cette h comme a très bien fonctionné sur notre projet ...

Bien sûr, le contraire pourrait également être fait, si tel était le cas (mais pas votre question).

SELECT * 
FROM A 
INNER JOIN udf_SplitVariable(@ExcludedList, ',') f ON A.Id = f.Value 

Et cela est vraiment pratique lorsque vous traitez des rapports qui ont une liste de paramètres multi-sélection optionnelle. Si le paramètre est NULL, vous souhaitez que toutes les valeurs soient sélectionnées, mais si vous avez défini une ou plusieurs valeurs, vous souhaitez que les données du rapport soient filtrées sur ces valeurs. Ensuite, utilisez SQL comme ceci:

SELECT * 
FROM A 
INNER JOIN udf_SplitVariable(@ExcludedList, ',') f ON A.Id = f.Value OR @ExcludeList IS NULL 

De cette façon, si @ExcludeList est une valeur NULL, la clause OR dans la jointure devient un interrupteur qui désactive le filtrage sur cette valeur. Très pratique ...

21

Voici un exemple où j'utilise la variable table pour lister plusieurs valeurs dans une clause IN. La raison évidente est de pouvoir changer la liste de valeurs seulement une place dans une longue procédure.

Pour le rendre encore plus dynamique et alowing entrée utilisateur, je suggère déclarer une variable varchar pour l'entrée, puis en utilisant un certain temps pour boucler cuvette les données dans la variable et l'insérer dans la table des variables .

Remplacez @your_list, Your_table et les valeurs par des valeurs réelles.

DECLARE @your_list TABLE (list varchar(25)) 
INSERT into @your_list 
VALUES ('value1'),('value2376') 

SELECT * 
FROM your_table 
WHERE your_column in (select list from @your_list) 

La sélection abowe instruction fera la même chose que:

SELECT * 
FROM your_table 
WHERE your_column in ('value','value2376') 
+0

cela mènera à la baisse des performances car la seconde sélection dans la clause in car elle sera exécutée pour chaque ligne – fatiDev

Questions connexes