C # SQL Server - Bir listeyi saklı yordama geçirme


112

C # kodumdan bir SQL Server saklı yordamı çağırıyorum:

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();

Depolanan proc'um oldukça standarttır, ancak bir birleştirme yapar QueryTable(bu nedenle depolanmış bir proc kullanma ihtiyacı).

Şimdi: List<string>Parametre kümesine bir dizi listesi eklemek istiyorum . Örneğin, depolanan proc sorgum şu şekildedir:

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

Bununla birlikte, dizelerin listesi dinamik ve birkaç yüz uzunluğundadır.

List<string>Depolanan proc'a dizelerin bir listesini iletmenin bir yolu var mı ??? Yoksa bunu yapmanın daha iyi bir yolu var mı?

Çok teşekkürler, Brett



SQL Server'ın hangi sürümü ? 2005 ?? 2008 ?? 2008 R2 ?? SQL Server 2008 ve daha yenisi "tablo değerli parametreler" konseptine sahiptir (ayrıntılar için Redth'in yanıtına bakın)
marc_s

İlgisiz ipucu: SqlDataReader ayrıca IDisposable olduğundan bir usingblok içinde olması gerekir .
Richardissimo

Yanıtlar:


179

SQL Server 2008 kullanıyorsanız, Kullanıcı Tanımlı Tablo Türü adında yeni bir özellik vardır. İşte nasıl kullanılacağına dair bir örnek:

Kullanıcı Tanımlı Tablo Türünüzü oluşturun:

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

Ardından, bunu saklı yordamınızda doğru şekilde kullanmanız gerekir:

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

Son olarak, c # 'da kullanmak için bazı sql'ler:

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());
          }
         }
    }
}

Bunu SSMS'den yürütmek için

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

10
Parametrenin değerini ayarlamak için bir datatable tanımlaması gerekiyor mu? Başka ışık yaklaşımı var mı?
ca9163d9

2
Denedik ancak dezavantajının Enitiy çerçevesi tarafından desteklenmediğini gördük
Bishoy Hanna

7
LINQ2SQL KULLANIYORSANIZ BU ÇÖZÜME DİKKATLİ OLUN, KULLANICI TARAFINDAN TANIMLANMIŞ TABLO TİPLERİNİ PARAMETRE OLARAK DESTEKLEMEZ !!! Jon Raynor'un cevabında virgülle ayrılmış listeler ve ayrıştırıcı işlevi kullanılarak bir çözüm bulunabilir, ancak bunun da dezavantajları vardır ....
Fazi

3
@Fazi bu durumda Linq2SQL kullanmayın. T-SQL'de dize birleştirme ve ayrıştırma tercih edilir
Panagiotis Kanavos

2
Evet, peki bunu aslında SSMS'den nasıl uyguluyorsunuz?
Sinaesthetic

21

Bu durumda tipik desen, öğeleri virgülle ayrılmış bir listeden geçirmek ve ardından SQL'de bunu kullanabileceğiniz bir tabloya ayırmaktır. Çoğu insan, genellikle bunu yapmak için belirli bir işlev oluşturur:

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

Ve sonra onu diğer sorgularda kullanabilirsiniz.


17
tamlık için bir dbo.SplitCommaString uygulaması için bir bağlantı vereyim: goo.gl/P9ROs
Veli Gebrev

3
Veri alanlarınızdan birinde virgül olduğunda ne olur?
Kevin Panko

3
Bunun yerine borularla sınırlandırın.
Alex In Paris

@AlexInParis Ya veri alanlarınızdan birinde bir boru varsa?
RayLoveless

2
Ardından veri alanlarınızda olmayan bir şey kullanın. Gerekirse verilerinizi temizleyin, ancak şimdiye kadar gördüğüm çok fazla veri boruları kullanmadı. Kesinlikle gerekliyse ¤ veya § gibi başka bir karakter bulun.
Alex In Paris

9

Hayır, diziler / listeler doğrudan SQL Server'a geçirilemez.

Aşağıdaki seçenekler mevcuttur:

  1. Virgülle sınırlanmış bir listeyi geçirmek ve ardından SQL'de bir işleve sahip olmak listeyi böler. Virgülle ayrılmış liste büyük olasılıkla bir Nvarchar () olarak geçirilecektir.
  2. XML'yi iletin ve SQL Server'da bir işleve sahip olun, listedeki her değer için XML'i ayrıştırın
  3. Yeni tanımlanmış Kullanıcı Tanımlı tablo türünü kullanın (SQL 2008)
  4. SQL'i dinamik olarak oluşturun ve ham listede "1,2,3,4" olarak geçirin ve SQL ifadesini oluşturun. Bu, SQL enjeksiyon saldırılarına eğilimlidir, ancak işe yarayacaktır.

2

Evet, Stored proc parametresini yapın VARCHAR(...) Ve sonra virgülle ayrılmış değerleri bir saklı yordama geçirin.

Sql Server 2008 kullanıyorsanız, TVP ( Tablo Değer Parametreleri ): SQL 2008 TVP ve LINQ, eğer QueryTable yapısı dizgi dizilerinden daha karmaşıksa, aksi halde SQl Server içinde tablo türünün oluşturulması gerektiğinden aşırı bir durum olur.


2

List yerine tek sütunlu bir datatable yapın ve tabloya dizeler ekleyin. Bu datatable'ı yapılandırılmış tip olarak geçirebilir ve tablonuzun başlık alanıyla başka bir birleştirme gerçekleştirebilirsiniz.


gitmenin yolu bu. Aslında db tarafında bir tablo oluşturdum ve sunucuya bcp write ile yükledim.
dier


0

Farkında olduğum tek yol, CSV listesi oluşturmak ve sonra onu dizge olarak geçirmek. Ardından, SP tarafında, sadece bölün ve ihtiyacınız olanı yapın.


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

tablo olarak bir TÜR oluşturun ve bunu "DizeListesi1" olarak adlandırın

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

Yukarıdaki gibi bir prosedür oluşturun ve bunu "UserStringList1" olarak adlandırın

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();
                    }

c #, bunu dene

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.