C # SQL Server - Chuyển một danh sách đến một thủ tục được lưu trữ


111

Tôi đang gọi một thủ tục được lưu trữ trên SQL Server từ mã C # của mình:

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 được lưu trữ của tôi khá chuẩn nhưng có tham gia với QueryTable(do đó cần sử dụng proc được lưu trữ).

Bây giờ: Tôi muốn thêm một danh sách các chuỗi List<string>, vào tập tham số. Ví dụ: truy vấn proc được lưu trữ của tôi như sau:

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

Tuy nhiên, danh sách các chuỗi là động và dài vài trăm.

Có cách nào để chuyển một danh sách các chuỗi List<string>đến proc được lưu trữ không ??? Hoặc là có một cách tốt hơn để làm điều này?

Cảm ơn nhiều, Brett



Có gì phiên bản của SQL Server ?? 2005 ?? Năm 2008 ?? 2008 R2 ?? SQL Server 2008 và mới hơn có khái niệm "các thông số bảng giá trị" (xem phản ứng Redth để biết chi tiết)
marc_s

Mẹo không liên quan: SqlDataReader cũng là IDisposable nên phải nằm trong một usingkhối.
Richardissimo

Câu trả lời:


178

Nếu bạn đang sử dụng SQL Server 2008, có một tính năng mới được gọi là Loại bảng do người dùng xác định. Đây là một ví dụ về cách sử dụng nó:

Tạo loại bảng do người dùng xác định:

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

Tiếp theo, bạn cần sử dụng nó đúng cách trong quy trình đã lưu trữ của mình:

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

Cuối cùng, đây là một số sql để sử dụng nó trong 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());
          }
         }
    }
}

Để thực hiện điều này từ 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
Nó có phải xác định một dữ liệu để đặt giá trị của tham số không? Bất kỳ cách tiếp cận ánh sáng khác?
ca9163d9 13/12/12

2
Chúng tôi đã thử nó nhưng chúng tôi thấy nhược điểm của nó không được hỗ trợ bởi khuôn khổ Enitiy
Bishoy Hanna

7
HÃY CẨN THẬN VỚI GIẢI PHÁP NÀY NẾU BẠN ĐANG SỬ DỤNG LINQ2SQL NHƯ NÓ KHÔNG HỖ TRỢ CÁC LOẠI BẢNG ĐỊNH NGHĨA CỦA NGƯỜI DÙNG NHƯ THÔNG SỐ !!! Một cách giải quyết có thể được tìm thấy trong câu trả lời Jon Raynor, sử dụng danh sách dấu phẩy tách ra và một chức năng phân tích cú pháp, tuy nhiên điều này cũng có nhược điểm ....
Fazi

3
@Fazi trong trường hợp này, không sử dụng Linq2SQL. Đó là một lợi thế để nối chuỗi và phân tích trong T-SQL
Panagiotis Kanavos

2
Vâng, vậy làm thế nào để bạn thực hiện điều này từ SSMS?
Sinaesthetic

21

Mô hình điển hình trong tình huống này là chuyển các phần tử trong danh sách được phân tách bằng dấu phẩy, sau đó trong SQL tách phần tử đó ra thành một bảng mà bạn có thể sử dụng. Hầu hết mọi người thường tạo một hàm cụ thể để thực hiện việc này như:

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

Và sau đó bạn có thể sử dụng nó trong các truy vấn khác.


17
hãy để tôi ném vào một liên kết cho một thực hiện dbo.SplitCommaString cho đầy đủ: goo.gl/P9ROs
Veli Gebrev

3
Và điều gì sẽ xảy ra khi có dấu phẩy trong một trong các trường dữ liệu của bạn?
Kevin Panko

3
Thay vào đó, hãy phân tách nó bằng các đường ống.
Alex ở Paris,

@AlexInParis Điều gì sẽ xảy ra nếu có một đường ống dẫn trong một trong các trường dữ liệu của bạn?
RayLoveless

2
Sau đó, sử dụng thứ gì đó không có trong các trường dữ liệu của bạn. Làm sạch dữ liệu của bạn, nếu cần thiết nhưng không có nhiều dữ liệu mà tôi từng thấy đã từng sử dụng đường ống. Nếu chúng thực sự cần thiết, hãy tìm một số ký tự khác như ¤ hoặc §.
Alex ở Paris,

9

Không, mảng / danh sách không thể được chuyển trực tiếp đến SQL Server.

Lựa chọn tiếp theo đã khả thi:

  1. Chuyển danh sách được phân tách bằng dấu phẩy và sau đó có một hàm trong SQL chia danh sách. Danh sách được phân tách bằng dấu phẩy rất có thể sẽ được chuyển dưới dạng Nvarchar ()
  2. Chuyển xml và có một hàm trong SQL Server phân tích cú pháp XML cho từng giá trị trong danh sách
  3. Sử dụng kiểu bảng mới do người dùng xác định (SQL 2008)
  4. Tự động xây dựng SQL và chuyển vào danh sách thô là "1,2,3,4" và xây dựng câu lệnh SQL. Điều này dễ bị tấn công SQL injection, nhưng nó sẽ hoạt động.

2

Đúng, đặt tham số Stored proc là VARCHAR(...) Và sau đó chuyển các giá trị được phân tách bằng dấu phẩy cho một thủ tục được lưu trữ.

Nếu bạn đang sử dụng Sql Server 2008, bạn có thể tận dụng TVP ( Tham số giá trị bảng ): SQL 2008 TVP và LINQ nếu cấu trúc của QueryTable phức tạp hơn mảng chuỗi, nếu không nó sẽ là quá mức cần thiết vì yêu cầu loại bảng được tạo trong SQl Server


2

Tạo một định dạng dữ liệu với một cột thay vì Danh sách và thêm chuỗi vào bảng. Bạn có thể chuyển dữ liệu này làm kiểu có cấu trúc và thực hiện một phép nối khác với trường tiêu đề của bảng của bạn.


đó là con đường để đi. Tôi thực sự đã tạo một bảng ở phía db và tải bcp ghi vào máy chủ.
dier


0

Cách duy nhất tôi biết là xây dựng danh sách CSV và sau đó chuyển nó dưới dạng chuỗi. Sau đó, về phía SP, chỉ cần tách nó ra và làm bất cứ điều gì bạn cần.


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

tạo TYPE dưới dạng bảng và đặt tên là "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

Tạo một thủ tục như trên và đặt tên là "UserStringList1" s

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

trong c #, hãy thử cái này

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.