2009-12-28 3 views
6

Le projet que je travaille sur a beaucoup de IN-requêtes telles que:Passez un tableau comme valeur dans un ado.net DBParameter

SELECT something, anotherthing 
FROM atable 
WHERE something IN (value1, value2, value3) 

Ceci est un exemple d'une requête avec 3 paramètres IN -part mais la même requête pourrait être exécutée avec 1 ou 2 ou 5 ou 10 ou ... paramètres. Le problème est que chaque requête a un autre plan d'exécution dans la base de données avec le ralentit.

Je voudrais Hava une requête comme ceci:

SELECT something, anotherthing 
FROM atable 
WHERE something IN (@value1, @value2, @value3) 

ou ceci:

SELECT something, anotherthing 
FROM atable 
WHERE something IN (@values) 

j'ai accompli la première requête avec une fonction d'aide, mais je dois encore différent plan d'exécution par nombre de paramètres. Cela pourrait être résolu avec le second.

Quelle est la meilleure façon de passer un tableau en tant que paramètre de base de données? J'utilise Oracle et SQL Server, les solutions pour les deux sont les bienvenues.

Répondre

4

Pour SQL Server, il existe deux approches communes pour cela. La troisième option à éviter est de passer dans un varchar et de le concaténer en une instruction SQL dynamique avec IN - il s'agit d'une surface d'attaque par injection claire.

options raisonnables:

  • passe dans un varchar et utiliser une UDF pour séparer les données sur un délimiteur (like in this question), peut-être par des virgules, pipe, onglet, etc. Inscrivez-vous au résultat:

    SELECT something, anotherthing 
    FROM atable a 
    INNER JOIN dbo.SplitUDF(@values) udf 
         ON udf.Value = a.something 
    
  • une utilisation table-valued-parameter (SQL2008) et rejoindre directement (éviter l'UDF)
+0

Malheureusement, je dois prendre en charge SQL Server 2000 et 2005, pas de paramètre de table pour moi. – Jochen

1

Jetez un oeil ces articles

C'est un exemple d'utilisation du type XML pour créer une liste

--Split 
DECLARE @textXML XML 
DECLARE @data NVARCHAR(MAX), 
     @delimiter NVARCHAR(5) 

SELECT @data = 'A,B,C', 
     @delimiter = ',' 

SELECT @textXML = CAST('<d>' + REPLACE(@data, @delimiter, '</d><d>') + '</d>' AS XML) 
SELECT T.split.value('.', 'nvarchar(max)') AS data 
FROM @textXML.nodes('/d') T(split) 
0

Ce code fait l'affaire. Vous pouvez créer votre propre fonction BuildQuery (???).

public void RemoveDependencies(int versionID, int[] deps) 
    { 
     if (versionID <= 0) 
      throw new ArgumentException(); 
     if (deps == null) 
      throw new ArgumentNullException(); 
     if (deps.Length <= 0) 
      throw new ArgumentException(); 

     SqlCommand cmd = new SqlCommand(); 
     string query = "DELETE FROM Dependencies WHERE version_id = @VersionId AND dep_version_id IN ("; 
     int n = deps.Length; 
     string key; 
     for (int i = 0; i < n; i++) 
     { 
      if (deps[i] <= 0) 
       throw new ArgumentException(); 
      key = String.Format("@dep{0}", i); 
      query += key; 
      cmd.Parameters.AddWithValue(key, deps[i]); 
      if (i < n - 1) 
      { 
       query += ", "; 
      } 
     } 
     query += ")"; 
     cmd.Parameters.AddWithValue("@VersionId", versionID); 
     cmd.CommandText = query; 
     using (SqlConnection con = GetSqlConnection()) 
     { 
      con.Open(); 
      cmd.Connection = con; 
      if (cmd.ExecuteNonQuery() <= 0) 
      { 
       throw new ArgumentException("No rows affected! Illegal id."); 
      } 
     } 
    } 
Questions connexes