2010-05-31 27 views
30

Let dichiarato dire ho ottenuto il seguente:SQL Server - In clausola con una variabile

DECLARE @ExcludedList VARCHAR(MAX) 

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

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

errore: Conversione non riuscita durante la conversione del valore varchar '' al tipo di dati int.

capisco perché l'errore è lì, ma non so come risolverlo ...

+0

@TomTom et al - Non sono d'accordo che questo è un duplicato. L'altra domanda riguarda più aspetti non correlati a ciò che questa domanda affronta nello specifico. Soprattutto, sono contento di aver trovato questo post e non l'altro - poiché questo risolveva il mio problema con precisione. – condiosluzverde

risposta

30

È necessario eseguire questo come un sp dinamico come

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

Non è vulnerabile a SQL Injection se ExcludedList è inserito da un utente? Ad esempio, ExcludedList = '3); UTENTI DI TABELLA A GOCCIA; - '(il sistema di commenti non mi consente di usare il segno (a)) – ristonj

+0

Non importa. Più variabili (una per articolo) o SQL dinamico sono gli UNICI modi per gestirlo. Sì, bisogna stare attenti. Oppure rilascia la clausola in (e carica gli elementi in una variabile tabella/tabella temporanea e poi partecipa). – TomTom

+0

Preferirei usare uno splitter per stringhe invece di sql dinamico per evitare l'iniezione sql. http://sqlperformance.com/2012/07/t-sql-queries/split-strings –

0

penso problema è nel

3 + ', ' + 4 

cambiamento a

'3' + ', ' + '4' 

DECLARE @ExcludedList VARCHAR(MAX) 

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

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

SET @ExcludedListe tale che la tua richiesta diventi

o

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

o

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

Che corregge l'istruzione set, ma la query select non funzionerà ancora –

+0

Questo ha funzionato brillantemente –

0

Prova questo:

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

E poi chiamare in questo modo:

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

Non è possibile utilizzare una variabile in un 012 Clausola- è necessario utilizzare dynamic SQL o utilizzare a function (TSQL or CLR) to convert the list of values into a table.

SQL dinamico esempio:

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) 

so che sto rispondendo a un vecchio post, ma ho voluto condividere un esempio di come utilizzare le tabelle delle variabili quando si vuole evitare l'uso di SQL dinamico. Non sono sicuro che sia il modo più efficace, tuttavia questo ha funzionato in passato per me quando l'SQL dinamico non era un'opzione.

+3

Mi piace questo perché si è evitato l'uso di SQL dinamico – MattK311

3

In primo luogo, creare una funzione rapida che dividere un elenco delimitato di valori in una tabella, come questo:

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 

quindi chiamare il funzione come questa ...

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

Questo h come ha funzionato davvero bene sul nostro progetto ...

Naturalmente, si potrebbe anche fare il contrario, se così fosse (anche se non è la tua domanda).

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

E questo è davvero utile quando si tratta di report che hanno una lista di parametri multi-selezione opzionale. Se il parametro è NULL, si desidera che tutti i valori siano selezionati, ma se ha uno o più valori si desidera che i dati del report vengano filtrati su tali valori. Quindi utilizzare SQL come questo:

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

questo modo, se @ExcludeList è un valore NULL, la clausola OR nel join diventa un interruttore che disattiva il filtraggio da questo valore. Molto utile ...

21

Questo è un esempio in cui utilizzo la variabile di tabella per elencare più valori in una clausola IN. L'ovvia ragione è di essere in grado di modificare l'elenco di valori solo in una procedura lunga.

Per rendere ancora più dinamico e alowing input dell'utente, suggerisco dichiara una variabile varchar per l'ingresso, e quindi utilizzando un po 'per ciclo attraverso gli dati nella variabile ed inserirla nella variabile di tabella.

Sostituisci @your_list, Your_table e i valori con elementi reali.

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) 

L'istruzione select abowe farà lo stesso come:

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

questo porterà a prestazioni in calo perché la seconda selezione all'interno della clausola in perché verrà eseguita per ogni riga – fatiDev

Problemi correlati