2010-04-08 7 views
3

Je suis en train d'utiliser sp_executesql pour empêcher l'injection SQL dans SQL 2005, j'ai une requête simple comme ceci:sp_executesql avec « IN » déclaration

SELECT * from table WHERE RegionCode in ('X101', 'B202') 

Cependant, quand je l'utilise sp_executesql pour exécuter ce qui suit, ne retourne rien.

Set @Cmd = N'SELECT * FROM table WHERE RegionCode in (@P1)' 
SET @ParamDefinition = N'@P1 varchar(100)'; 
DECLARE @Code as nvarchar(100); 
SET @Code = 'X101,B202' 
EXECUTE sp_executesql @Cmd, @ParamDefinition, @P1 = @Code 

Le est ce que je l'ai testé:

SET @Code = 'X101' <-- This works, it returns a single region 
SET @Code = 'X101,B202' <--- Returns nothing 
SET @Code = '''X101'',''B202''' <-- Returns nothing 

S'il vous plaît aider .... qu'est-ce que je fait de mal?

+0

Avez-vous trouvé ce problème résolu, ou pouvez-vous ajouter plus de détails à la question? –

Répondre

3

La raison pour laquelle cela ne fonctionne pas est que @ P1 est traité comme une seule valeur.

par exemple. Lorsque @Code est X101, B202 alors la requête est en cours d'exécution comme: SELECT * FROM Table WHERE RegionCode IN ('X101, B202') Donc, il cherche un RegionCode avec la valeur qui est contenue avec @ P1. Même lorsque vous incluez des guillemets simples, tout ce qui signifie que la valeur recherchée dans RegionCode doit contenir ces guillemets simples.

Vous auriez besoin de concaténer effectivement la variable @code dans le texte de commande SQL @cmd pour que cela fonctionne de la façon dont vous pensez:

SET @Code = '''X101'',''B202''' 
SET @Cmd = 'SELECT * FROM Table WHERE RegionCode IN (' + @Code + ')' 
EXECUTE (@Cmd) 

Il est évident que, ce que vous ouvre jusqu'à Injection SQL, vous devez donc être très prudent si vous avez adopté cette approche pour vous assurer que vous vous prémunir contre cela.

Il existe d'autres manières de gérer cette situation lorsque vous souhaitez transmettre une liste dynamique de valeurs à rechercher. Consultez les exemples sur my blog pour deux approches que vous pouvez utiliser avec SQL Server 2005. L'une consiste à passer une liste CSV sous la forme "Value1, Value2, Value3" que vous divisez ensuite en une variable TABLE en utilisant un utilisateur fonction définie (il y a beaucoup de mentions de cette approche si vous faites un google rapide ou une recherche de ce site). Une fois séparé, vous rejoignez alors cette variable TABLE à votre requête principale. La seconde approche consiste à transmettre un blob XML contenant les valeurs et à utiliser la fonctionnalité XML intégrée de SQL Server. Ces deux approches sont démontrées avec des métriques de performance dans ce lien, et elles ne nécessitent aucun SQL dynamique.

Si vous utilisiez SQL Server 2008, les paramètres de valeur de table seraient le chemin à suivre - c'est la troisième approche que je démontre dans ce lien qui ressort le mieux.

1

Il semble que le problème soit le seul paramètre. En effet, vous se retrouver avec:

SELECT * from table WHERE RegionCode in ('X101,B202') 

ou

SELECT * from table WHERE RegionCode in ('''X101'', ''B202''') 

C'est, RegionCode doit être égale à 'X101,B202' ou ''X101','B202'' (la chaîne complète) au travail.

Vous êtes le meilleur pari est d'utiliser deux paramètres ici:

Set @Cmd = N'SELECT * FROM table WHERE RegionCode in (@P1,@P2)' 
SET @Code1 = 'X101' 
SET @Code2 = 'B202' 

Si vous allez avoir plus de deux éléments dans cette liste cependant, vous voudrez peut-être aller un autre itinéraire, probablement avec le temp tables ou paramètres de table.

2

Il existe plusieurs façons de séparer une chaîne dans SQL Server. Cet article porte sur les avantages et les inconvénients de chaque méthode à peu près:

"Arrays and Lists in SQL Server 2005 and Beyond, When Table Value Parameters Do Not Cut it" by Erland Sommarskog

Vous devez créer une fonction split. Voici comment une fonction split peut être utilisé:

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

I prefer the number table approach to split a string in TSQL mais il existe de nombreuses façons de diviser les chaînes dans SQL Server, voir le lien précédent, ce qui explique les avantages et les inconvénients de chacun.

Pour la méthode de table de nombres au travail, vous devez faire une configuration de table de temps, ce qui va créer une table Numbers qui contient des lignes de 1 à 10 000:

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 split:

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 diviser une chaîne CSV dans une table et se joindre à ou de l'utiliser mais vous avez besoin, même de l'intérieur sql dynamique. Voici comment l'utiliser dans la requête paramétrées dynamique de votre question:

DECLARE @Cmd as nvarchar(1000),@ParamDefinition nvarchar(1000); 
Set @Cmd = N'SELECT * FROM table WHERE RegionCode in (SELECT ListValue FROM dbo.FN_ListToTable('','',@P1))' 
SET @ParamDefinition = N'@P1 varchar(100)'; 
DECLARE @Code as nvarchar(1000); 
SET @Code = 'X101,B202' 
EXECUTE sp_executesql @Cmd, @ParamDefinition, @P1 = @Code 

Voici un échantillon de travail pour essayer (doit avoir la table des numéros et la configuration de fonction split en premier):

CREATE TABLE YourTable (PK int primary key, RowValue varchar(5)) 
INSERT YourTable VALUES (1,'A') 
INSERT YourTable VALUES (2,'BB') 
INSERT YourTable VALUES (3,'CCC') 
INSERT YourTable VALUES (4,'DDDD') 
INSERT YourTable VALUES (5,'EEE') 
INSERT YourTable VALUES (6,'FF') 
INSERT YourTable VALUES (7,'G') 

DECLARE @SQL    nvarchar(1000) 
     ,@ParamDefinition nvarchar(1000) 
     ,@ParamValue  varchar(100) 
SELECT @SQL = N'SELECT * FROM YourTable WHERE PK IN (SELECT ListValue FROM dbo.FN_ListToTable('','',@P1))' 
     ,@ParamDefinition = N'@P1 varchar(100)' 
     ,@ParamValue = '2,4,,,6,,8,,2,,4' 
EXECUTE sp_executesql @SQL, @ParamDefinition, @P1 = @ParamValue 

OUTPUT:

PK   RowValue 
----------- -------- 
2   BB 
4   DDDD 
6   FF 

(3 row(s) affected)