web-dev-qa-db-fra.com

C # SQL Server - Passage d'une liste à une procédure stockée

J'appelle une procédure stockée SQL Server à partir de mon code C #:

using (SqlConnection conn = new SqlConnection(connstring))
{
   conn.Open();
   using (SqlCommand cmd = new SqlCommand("InsertQuerySPROC", conn))
   {
      cmd.CommandType = CommandType.StoredProcedure;

      var STableParameter = cmd.Parameters.AddWithValue("@QueryTable", QueryTable);
      var NDistanceParameter = cmd.Parameters.AddWithValue("@NDistanceThreshold", NDistanceThreshold);
      var RDistanceParameter = cmd.Parameters.AddWithValue(@"RDistanceThreshold", RDistanceThreshold);

      STableParameter .SqlDbType = SqlDbType.Structured;
      NDistanceParameter.SqlDbType = SqlDbType.Int;
      RDistanceParameter.SqlDbType = SqlDbType.Int;

      // Execute the query
      SqlDataReader QueryReader = cmd.ExecuteReader();

Mon proc stocké est assez standard mais fait une jointure avec QueryTable (d'où la nécessité d'utiliser un proc stocké).

Maintenant: je veux ajouter une liste de chaînes, List<string>, au jeu de paramètres. Par exemple, ma requête proc stockée va comme ceci:

SELECT feature 
FROM table1 t1 
INNER JOIN @QueryTable t2 ON t1.fid = t2.fid 
WHERE title IN <LIST_OF_STRINGS_GOES_HERE>

Cependant, la liste des chaînes est dynamique et longue de quelques centaines.

Existe-t-il un moyen de transmettre une liste de chaînes List<string> au processus stocké ??? Ou existe-t-il un meilleur moyen de le faire?

Merci beaucoup, Brett

103
Brett

Si vous utilisez SQL Server 2008, une nouvelle fonctionnalité appelée type de table définie par l'utilisateur est présente. Voici un exemple d'utilisation:

Créez votre type de table défini par l'utilisateur:

CREATE TYPE [dbo].[StringList] AS TABLE(
    [Item] [NVARCHAR](MAX) NULL
);

Ensuite, vous devez l’utiliser correctement dans votre procédure stockée:

CREATE PROCEDURE [dbo].[sp_UseStringList]
    @list StringList READONLY
AS
BEGIN
    -- Just return the items we passed in
    SELECT l.Item FROM @list l;
END

Enfin, voici quelques fichiers SQL à utiliser en c #:

using (var con = new SqlConnection(connstring))
{
    con.Open();

    using (SqlCommand cmd = new SqlCommand("exec sp_UseStringList @list", con))
    {
        using (var table = new DataTable()) {
          table.Columns.Add("Item", typeof(string));

          for (int i = 0; i < 10; i++)
            table.Rows.Add("Item " + i.ToString());

          var pList = new SqlParameter("@list", SqlDbType.Structured);
          pList.TypeName = "dbo.StringList";
          pList.Value = table;

          cmd.Parameters.Add(pList);

          using (var dr = cmd.ExecuteReader())
          {
            while (dr.Read())
                Console.WriteLine(dr["Item"].ToString());
          }
         }
    }
}

Pour l'exécuter depuis SSMS

DECLARE @list AS StringList

INSERT INTO @list VALUES ('Apple')
INSERT INTO @list VALUES ('Banana')
INSERT INTO @list VALUES ('Orange')

-- Alternatively, you can populate @list with an INSERT-SELECT
INSERT INTO @list
   SELECT Name FROM Fruits

EXEC sp_UseStringList @list
165
Redth

Le modèle typique dans cette situation est de passer les éléments dans une liste délimitée par des virgules, puis en SQL le scinde en une table que vous pouvez utiliser. La plupart des gens créent généralement une fonction spécifiée comme ceci:

 INSERT INTO <SomeTempTable>
 SELECT item FROM dbo.SplitCommaString(@myParameter)

Et vous pouvez ensuite l'utiliser dans d'autres requêtes.

20
Tejs

Non, les tableaux/listes ne peuvent pas être transmis directement à SQL Server.

Les options suivantes sont disponibles:

  1. Passer une liste délimitée par des virgules, puis avoir une fonction SQL divise la liste. La liste délimitée par des virgules sera très probablement passée en tant que Nvarchar ()
  2. Passer xml et avoir une fonction dans SQL Server analyser le XML pour chaque valeur de la liste
  3. Utiliser le nouveau type de table défini par l'utilisateur (SQL 2008)
  4. Générez dynamiquement le code SQL et transmettez-lui la liste brute sous la forme "1,2,3,4", puis générez l'instruction SQL. Ceci est sujet aux attaques par injection SQL, mais cela fonctionnera.
7
Jon Raynor

Oui, définissez le paramètre de procédure stockée sous la forme VARCHAR(...), puis transmettez les valeurs séparées par des virgules à une procédure stockée.

Si vous utilisez Sql Server 2008, vous pouvez utiliser TVP ( Paramètres de valeur de table ): SQL 2008 TVP et LINQ si la structure de QueryTable est plus complexe qu'un tableau de chaînes, sinon ce serait un dépassement car la création d'un type de table est requise dans SQl Server

2
sll

Si vous préférez diviser une liste CSV en SQL, il existe une façon différente de le faire en utilisant Common Table Expressions (CTE). Voir Méthode efficace pour scinder une chaîne en utilisant CTE .

1
Larry Silverman

Faites un datatable avec une colonne au lieu de List et ajoutez des chaînes à la table. Vous pouvez passer ce type de données comme type structuré et effectuer une autre jointure avec le champ title de votre table.

1
hungryMind

La seule façon dont je suis au courant est de construire une liste CSV, puis de la transmettre sous forme de chaîne. Ensuite, du côté SP, divisez-le et faites ce que vous voulez.

0
Andrey Agibalov
CREATE TYPE [dbo].[StringList1] AS TABLE(
[Item] [NVARCHAR](MAX) NULL,
[counts][nvarchar](20) NULL);

créez un TYPE en tant que table et nommez-le "StringList1"

create PROCEDURE [dbo].[sp_UseStringList1]
@list StringList1 READONLY
AS
BEGIN
    -- Just return the items we passed in
    SELECT l.item,l.counts FROM @list l;
    SELECT l.item,l.counts into tempTable FROM @list l;
 End

Créez une procédure comme ci-dessus et nommez-la "UserStringList1"

String strConnection = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString.ToString();
            SqlConnection con = new SqlConnection(strConnection);
            con.Open();
            var table = new DataTable();

            table.Columns.Add("Item", typeof(string));
            table.Columns.Add("count", typeof(string));

            for (int i = 0; i < 10; i++)
            {
                table.Rows.Add(i.ToString(), (i+i).ToString());

            }
                SqlCommand cmd = new SqlCommand("exec sp_UseStringList1 @list", con);


                    var pList = new SqlParameter("@list", SqlDbType.Structured);
                    pList.TypeName = "dbo.StringList1";
                    pList.Value = table;

                    cmd.Parameters.Add(pList);
                    string result = string.Empty;
                    string counts = string.Empty;
                    var dr = cmd.ExecuteReader();

                    while (dr.Read())
                    {
                        result += dr["Item"].ToString();
                        counts += dr["counts"].ToString();
                    }

dans le c #, essayez ceci

0
Deepalakshmi