C # SQL Server - ส่งผ่านรายการไปยังโพรซีเดอร์ที่เก็บไว้


112

ฉันกำลังเรียกใช้กระบวนงานที่จัดเก็บ SQL Server จากรหัส 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();

proc ที่เก็บไว้ของฉันค่อนข้างเป็นมาตรฐาน แต่เข้าร่วมด้วยQueryTable(ดังนั้นความจำเป็นในการใช้ proc ที่เก็บไว้)

ตอนนี้: ฉันต้องการเพิ่มรายการสตริงList<string>ให้กับชุดพารามิเตอร์ ตัวอย่างเช่นแบบสอบถาม proc ที่เก็บไว้ของฉันจะเป็นดังนี้:

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

อย่างไรก็ตามรายการสตริงเป็นแบบไดนามิกและมีความยาวไม่กี่ร้อย

มีวิธีส่งรายการสตริงList<string>ไปยัง proc ที่เก็บไว้หรือไม่ ??? หรือมีวิธีที่ดีกว่านี้หรือไม่?

ขอบคุณมาก Brett


อาจซ้ำกันของstackoverflow.com/questions/209686/…
Andrey Agibalov

SQL Server รุ่นอะไร?? พ.ศ. 2548 ?? พ.ศ. 2551 ?? 2008 R2 ?? SQL Server 2008 และใหม่กว่ามีแนวคิด "table-worthed parameters" (ดูคำตอบของ Redth สำหรับรายละเอียด)
marc_s

เคล็ดลับที่ไม่เกี่ยวข้อง: SqlDataReader ยังเป็น IDisposable ดังนั้นควรอยู่ในusingบล็อก
Richardissimo

คำตอบ:


179

หากคุณใช้ SQL Server 2008 จะมีคุณลักษณะใหม่ที่เรียกว่า User Defined Table Type นี่คือตัวอย่างวิธีการใช้งาน:

สร้างประเภทตารางที่ผู้ใช้กำหนด:

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

ถัดไปคุณต้องใช้อย่างถูกต้องในขั้นตอนการจัดเก็บของคุณ:

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

สุดท้ายนี่คือ sql บางส่วนที่จะใช้ใน 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());
          }
         }
    }
}

ในการดำเนินการนี้จาก 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

10
ต้องกำหนด datatable เพื่อกำหนดค่าของพารามิเตอร์หรือไม่? วิธีแสงอื่น ๆ ?
ca9163d9

2
เราลองใช้แล้ว แต่พบข้อเสียที่ไม่ได้รับการสนับสนุนจาก Enitiy framework
Bishoy Hanna

7
ระมัดระวังกับโซลูชันนี้หากคุณใช้ LINQ2SQL เนื่องจากไม่รองรับตารางที่กำหนดโดยผู้ใช้เป็นพารามิเตอร์ !!! วิธีแก้ปัญหาสามารถพบได้ในคำตอบของ Jon Raynor โดยใช้รายการที่คั่นด้วยจุลภาคและฟังก์ชัน parser แต่ก็มีข้อเสียเช่นกัน ....
Fazi

3
@Fazi ในกรณีนี้อย่าใช้ Linq2SQL ควรใช้การต่อสตริงและแยกวิเคราะห์ใน T-SQL
Panagiotis Kanavos

2
ใช่แล้วคุณจะเรียกใช้สิ่งนี้จาก SSMS ได้อย่างไร?
Sinaesthetic

21

รูปแบบทั่วไปในสถานการณ์นี้คือการส่งผ่านองค์ประกอบในรายการที่คั่นด้วยจุลภาคจากนั้นใน SQL จะแยกสิ่งนั้นออกเป็นตารางที่คุณสามารถใช้ได้ คนส่วนใหญ่มักสร้างฟังก์ชันเฉพาะสำหรับการทำสิ่งนี้:

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

จากนั้นคุณสามารถใช้ในแบบสอบถามอื่น ๆ


17
ให้ฉันส่งลิงค์สำหรับการใช้งาน dbo.SplitCommaString เพื่อความสมบูรณ์: goo.gl/P9ROs
Veli Ge ชื่อย่อ

3
และจะเกิดอะไรขึ้นเมื่อมีเครื่องหมายจุลภาคในช่องข้อมูลของคุณ
Kevin Panko

3
คั่นด้วยท่อแทน
Alex In Paris

@AlexInParis เกิดอะไรขึ้นถ้ามีท่อในช่องข้อมูลของคุณ?
RayLoveless

2
จากนั้นใช้สิ่งที่ไม่อยู่ในช่องข้อมูลของคุณ ล้างข้อมูลของคุณหากจำเป็น แต่มีข้อมูลไม่มากเท่าที่ฉันเคยเห็นเคยใช้ไปป์ หากจำเป็นจริงๆให้หาอักขระอื่นเช่น¤หรือ§
Alex In Paris

9

ไม่ไม่สามารถส่งอาร์เรย์ / รายการไปยัง SQL Server ได้โดยตรง

มีตัวเลือกดังต่อไปนี้:

  1. การส่งผ่านรายการที่คั่นด้วยจุลภาคจากนั้นให้ฟังก์ชันใน SQL แยกรายการ รายการที่คั่นด้วยจุลภาคมักจะถูกส่งผ่านเป็น Nvarchar ()
  2. ส่ง xml และมีฟังก์ชันใน SQL Server แยกวิเคราะห์ XML สำหรับแต่ละค่าในรายการ
  3. ใช้ชนิดตารางที่กำหนดโดยผู้ใช้ที่กำหนดใหม่ (SQL 2008)
  4. สร้าง SQL แบบไดนามิกและส่งผ่านรายการดิบเป็น "1,2,3,4" และสร้างคำสั่ง SQL สิ่งนี้มีแนวโน้มที่จะโจมตีด้วยการฉีด SQL แต่จะได้ผล

2

ใช่สร้างพารามิเตอร์ Stored proc เป็นVARCHAR(...) แล้วส่งค่าที่คั่นด้วยจุลภาคไปยังกระบวนงานที่เก็บไว้

หากคุณใช้ Sql Server 2008 คุณสามารถใช้ประโยชน์จาก TVP ( Table Value Parameters ): SQL 2008 TVP และ LINQหากโครงสร้างของ QueryTable ซับซ้อนกว่าอาร์เรย์ของสตริงมิฉะนั้นจะเป็นการ overkill เนื่องจากต้องใช้ประเภทตารางในการสร้างภายใน SQl Server


2

สร้างข้อมูลที่มีคอลัมน์เดียวแทนรายการและเพิ่มสตริงลงในตาราง คุณสามารถส่งผ่านข้อมูลนี้เป็นประเภทที่มีโครงสร้างและดำเนินการรวมกับฟิลด์หัวเรื่องของตารางของคุณ


นั่นคือวิธีที่จะไป ฉันสร้างตารางที่ด้านฐานข้อมูลและโหลดด้วย bcp write to server
dier

1

หากคุณต้องการแยกรายการ CSV ใน SQL มีวิธีอื่นในการทำโดยใช้Common Table Expressions (CTEs) ดูวิธีการที่มีประสิทธิภาพเพื่อสตริงแยกโดยใช้ CTE


ขอบคุณ. เปลี่ยนเป็นคำถามสแตกล้นแทน
Larry Silverman

0

วิธีเดียวที่ฉันทราบคือการสร้างรายการ CSV แล้วส่งเป็นสตริง จากนั้นในฝั่ง SP ให้แยกมันและทำสิ่งที่คุณต้องการ


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

สร้าง TYPE เป็นตารางและตั้งชื่อเป็น "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

สร้างโพรซีเดอร์ด้านบนและตั้งชื่อเป็น "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();
                    }

ใน c # ลองทำเช่นนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.