ผ่านพารามิเตอร์อาร์เรย์ไปยังกระบวนงานที่เก็บไว้


53

ฉันมีกระบวนการที่คว้าบันทึกจำนวนมาก (1,000 รายการ) และดำเนินการกับพวกเขาและเมื่อฉันทำเสร็จแล้วฉันต้องทำเครื่องหมายเป็นจำนวนมากว่าผ่านการประมวลผล ฉันสามารถระบุสิ่งนี้กับรายการรหัสขนาดใหญ่ ฉันกำลังพยายามหลีกเลี่ยงรูปแบบ "การอัพเดทในวง" ดังนั้นฉันจึงต้องการหาวิธีที่มีประสิทธิภาพมากขึ้นในการส่งถุง ID นี้ไปยัง MS SQL Server 2008 ที่เก็บไว้ proc

ข้อเสนอ # 1 - พารามิเตอร์ที่มีมูลค่าของตาราง ฉันสามารถกำหนดประเภทตารางที่มีเพียง ID ฟิลด์และส่งตารางที่เต็มไปด้วย ID เพื่ออัปเดต

ข้อเสนอ # 2 - พารามิเตอร์ XML (varchar) พร้อม OPENXML () ในเนื้อหาของ proc

ข้อเสนอ # 3 - การแยกวิเคราะห์รายการ ฉันควรหลีกเลี่ยงสิ่งนี้ถ้าเป็นไปได้เพราะดูเหมือนว่าจะเทอะทะและผิดพลาดได้ง่าย

การตั้งค่าใด ๆ ในหมู่เหล่านี้หรือความคิดใด ๆ ที่ฉันพลาดไป?


คุณได้รับรายการรหัสขนาดใหญ่ได้อย่างไร
Larry Coleman

ฉันดึงพวกเขาลงพร้อมกับข้อมูล "น้ำหนักบรรทุก" ผ่าน proc ที่เก็บไว้อีก ฉันไม่จำเป็นต้องอัปเดตข้อมูลทั้งหมด แต่เพียงอัปเดตค่าสถานะในบางระเบียน
D. Lambert

คำตอบ:


42

บทความที่ดีที่สุดเท่าที่เคยมีมาในเรื่องนี้คือโดย Erland Sommarskog:

เขาครอบคลุมตัวเลือกทั้งหมดและอธิบายได้ค่อนข้างดี

ขออภัยสำหรับคำตอบสั้น ๆ แต่บทความของ Erland เกี่ยวกับ Arrays ก็เหมือนกับหนังสือของ Joe Celko บนต้นไม้และ SQL อื่น ๆ ถือว่า :)


23

มีการอภิปรายที่ดีเกี่ยวกับเรื่องนี้ในStackOverflowซึ่งครอบคลุมวิธีการมากมาย หนึ่งฉันต้องการสำหรับ SQL Server 2008+ คือการใช้ตารางมูลค่าพารามิเตอร์ นี่เป็นวิธีการแก้ปัญหาของ SQL Server ที่เป็นปัญหาของคุณ - ผ่านรายการค่าไปยังกระบวนงานที่เก็บไว้

ข้อดีของวิธีนี้คือ:

  • ทำการเรียกโพรซีเดอร์ที่เก็บไว้หนึ่งรายการด้วยข้อมูลทั้งหมดของคุณที่ส่งผ่านเป็นพารามิเตอร์ 1 ตัว
  • อินพุตตารางมีโครงสร้างและพิมพ์อย่างยิ่ง
  • ไม่มีการสร้างสตริง / การแยกวิเคราะห์หรือการจัดการของ XML
  • สามารถใช้อินพุตตารางเพื่อกรองเข้าร่วมหรืออะไรก็ได้

อย่างไรก็ตามรับทราบ:ถ้าคุณเรียกขั้นตอนการจัดเก็บที่ใช้ TVPs ผ่าน ADO.NET หรือ ODBC และดูกิจกรรมกับ SQL Server Profiler คุณจะสังเกตเห็นว่า SQL Server ได้รับหลาย ๆINSERTคำสั่งให้โหลด TVP หนึ่งรายการสำหรับแต่ละแถว ใน TVPตามด้วยการเรียกไปยังขั้นตอน นี่คือโดยการออกแบบ ชุดของINSERTs นี้จะต้องรวบรวมทุกครั้งที่มีการเรียกขั้นตอนและถือเป็นค่าใช้จ่ายเล็ก ๆ อย่างไรก็ตามแม้จะมีค่าใช้จ่ายนี้ TVPs ยังคงระเบิดวิธีการอื่น ๆ ในแง่ของประสิทธิภาพและการใช้งานสำหรับกรณีการใช้งานส่วนใหญ่

หากคุณต้องการเรียนรู้เพิ่มเติม Erland Sommarskog มีความผอมในการทำงานของพารามิเตอร์ที่มีค่าของตารางและให้ตัวอย่างหลายอย่าง

นี่คืออีกตัวอย่างที่ฉันปรุง:

CREATE TYPE id_list AS TABLE (
    id int NOT NULL PRIMARY KEY
);
GO

CREATE PROCEDURE [dbo].[tvp_test] (
      @param1           INT
    , @customer_list    id_list READONLY
)
AS
BEGIN
    SELECT @param1 AS param1;

    -- join, filter, do whatever you want with this table 
    -- (other than modify it)
    SELECT *
    FROM @customer_list;
END;
GO

DECLARE @customer_list id_list;

INSERT INTO @customer_list (
    id
)
VALUES (1), (2), (3), (4), (5), (6), (7);

EXECUTE [dbo].[tvp_test]
      @param1 = 5
    , @customer_list = @customer_list
;
GO

DROP PROCEDURE dbo.tvp_test;
DROP TYPE id_list;
GO

เมื่อฉันเรียกใช้สิ่งนี้ฉันได้รับข้อผิดพลาด: ข่าวสารเกี่ยวกับ 2715, ระดับ 16, สถานะ 3, โพรซีเดอร์ tvp_test, บรรทัดที่ 4 [Batch Start Line 4] คอลัมน์, พารามิเตอร์หรือตัวแปร # 2: ไม่พบชนิดข้อมูล id_list พารามิเตอร์หรือตัวแปร '@customer_list' มีประเภทข้อมูลที่ไม่ถูกต้อง ข่าวสารเกี่ยวกับ 1087, ระดับ 16, สถานะ 1, โพรซีเดอร์ tvp_test, บรรทัดที่ 13 [Batch Start Line 4] ต้องประกาศตัวแปรตาราง "@customer_list"
เดเมียน

@Damian - CREATE TYPEคำสั่งที่เริ่มต้นทำงานสำเร็จหรือไม่ คุณใช้ SQL Server เวอร์ชันใดอยู่
Nick Chammas

ในรหัส SP คุณมีประโยคนี้อินไลน์ `SELECT @ param1 AS param1; ' . จุดประสงค์คืออะไร? คุณไม่ได้ใช้งานหรือ param1 ดังนั้นทำไมคุณถึงใส่พารามิเตอร์นี้ในส่วนหัวของ SP
EAmez

@ AEAZ - มันเป็นเพียงตัวอย่างโดยพลการ ประเด็นก็คือไม่ได้@customer_list @param1ตัวอย่างแสดงให้เห็นว่าคุณสามารถผสมประเภทพารามิเตอร์ต่าง ๆ ได้
Nick Chammas

21

เรื่องทั้งหมดจะถูกกล่าวถึงในบทความที่ชัดเจนโดย Erland Sommarskog: "อาร์เรย์และรายชื่อใน SQL Server" เลือกรุ่นที่จะเลือก

สรุปสำหรับSQL Server 2008 ก่อนที่ TVPs จะจัดการส่วนที่เหลือ

  • CSV แยกตามที่คุณต้องการ (โดยทั่วไปฉันใช้ตารางตัวเลข)
  • XML และแยกวิเคราะห์ (ดีกว่ากับ SQL Server 2005+)
  • สร้างตารางชั่วคราวบนไคลเอนต์

บทความนี้มีค่าควรอ่านเพื่อดูเทคนิคและความคิดอื่น ๆ

แก้ไข: คำตอบที่ล่าช้าสำหรับรายการขนาดใหญ่ที่อื่น: การส่งพารามิเตอร์อาเรย์ไปยังโพรซีเดอร์ที่เก็บไว้


14

ฉันรู้ว่าฉันมาสายสำหรับงานปาร์ตี้นี้ แต่ฉันมีปัญหาดังกล่าวในอดีตต้องส่งตัวเลขตัวโต 100K ขึ้นไปและทำการวัดประสิทธิภาพบางอย่าง เราลงเอยด้วยการส่งพวกเขาในรูปแบบไบนารี่ในฐานะที่เป็นภาพซึ่งเร็วกว่าทุกอย่างในจำนวนมากถึง 100K

นี่คือรหัสเก่า (SQL Server 2005) ของฉัน:

SELECT  Number * 8 + 1 AS StartFrom ,
        Number * 8 + 8 AS MaxLen
INTO    dbo.ParsingNumbers
FROM    dbo.Numbers
GO

CREATE FUNCTION dbo.ParseImageIntoBIGINTs ( @BIGINTs IMAGE )
RETURNS TABLE
AS RETURN
    ( SELECT    CAST(SUBSTRING(@BIGINTs, StartFrom, 8) AS BIGINT) Num
      FROM      dbo.ParsingNumbers
      WHERE     MaxLen <= DATALENGTH(@BIGINTs)
    )
GO

รหัสต่อไปนี้คือการบรรจุจำนวนเต็มลงในไบนารีหยด ฉันกลับคำสั่งของไบต์ที่นี่:

static byte[] UlongsToBytes(ulong[] ulongs)
{
int ifrom = ulongs.GetLowerBound(0);
int ito   = ulongs.GetUpperBound(0);
int l = (ito - ifrom + 1)*8;
byte[] ret = new byte[l];
int retind = 0;
for(int i=ifrom; i<=ito; i++)
{
ulong v = ulongs[i];
ret[retind++] = (byte) (v >> 0x38);
ret[retind++] = (byte) (v >> 0x30);
ret[retind++] = (byte) (v >> 40);
ret[retind++] = (byte) (v >> 0x20);
ret[retind++] = (byte) (v >> 0x18);
ret[retind++] = (byte) (v >> 0x10);
ret[retind++] = (byte) (v >> 8);
ret[retind++] = (byte) v;
}
return ret;
}

9

ฉันฉีกขาดระหว่างการแนะนำคุณให้รู้จัก SO หรือตอบคำถามที่นี่ 'เพราะนี่เป็นคำถามการเขียนโปรแกรม แต่เนื่องจากฉันมีวิธีแก้ปัญหาแล้วฉันจึงใช้ ... ฉันจะโพสต์สิ่งนั้น;)

วิธีนี้ใช้งานได้คือคุณป้อนสตริงที่คั่นด้วยจุลภาค (แยกง่ายไม่แยกสไตล์ CSV) ในขั้นตอนการจัดเก็บเป็น varchar (4000) แล้วฟีดรายการที่เป็นฟังก์ชันนี้และได้รับตารางที่มีประโยชน์ออกมา ตารางของ varchars เพียง

สิ่งนี้ช่วยให้คุณสามารถส่งค่าของรหัสที่คุณต้องการประมวลผลและคุณสามารถทำการเข้าร่วมแบบง่าย ๆ ณ จุดนั้น

อีกทางหนึ่งคุณสามารถทำบางสิ่งด้วย CLR DataTable และฟีดข้อมูลนั้น แต่นั่นเป็นค่าใช้จ่ายที่มากกว่าในการสนับสนุนและทุกคนเข้าใจรายการ CSV

USE [Database]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER FUNCTION [dbo].[splitListToTable] (@list      nvarchar(MAX), @delimiter nchar(1) = N',')
      RETURNS @tbl TABLE (value     varchar(4000)      NOT NULL) AS
/*
http://www.sommarskog.se/arrays-in-sql.html
This guy is apparently THE guy in SQL arrays and lists 

Need an easy non-dynamic way to split a list of strings on input for comparisons

Usage like thus:

DECLARE @sqlParam VARCHAR(MAX)
SET @sqlParam = 'a,b,c'

SELECT * FROM (

select 'a' as col1, '1' as col2 UNION
select 'a' as col1, '2' as col2 UNION
select 'b' as col1, '3' as col2 UNION
select 'b' as col1, '4' as col2 UNION
select 'c' as col1, '5' as col2 UNION
select 'c' as col1, '6' as col2 ) x 
WHERE EXISTS( SELECT value FROM splitListToTable(@sqlParam,',') WHERE x.col1 = value )

*/
BEGIN
   DECLARE @endpos   int,
           @startpos int,
           @textpos  int,
           @chunklen smallint,
           @tmpstr   nvarchar(4000),
           @leftover nvarchar(4000),
           @tmpval   nvarchar(4000)

   SET @textpos = 1
   SET @leftover = ''
   WHILE @textpos <= datalength(@list) / 2
   BEGIN
      SET @chunklen = 4000 - datalength(@leftover) / 2
      SET @tmpstr = @leftover + substring(@list, @textpos, @chunklen)
      SET @textpos = @textpos + @chunklen

      SET @startpos = 0
      SET @endpos = charindex(@delimiter, @tmpstr)

      WHILE @endpos > 0
      BEGIN
         SET @tmpval = ltrim(rtrim(substring(@tmpstr, @startpos + 1,
                                             @endpos - @startpos - 1)))
         INSERT @tbl (value) VALUES(@tmpval)
         SET @startpos = @endpos
         SET @endpos = charindex(@delimiter, @tmpstr, @startpos + 1)
      END

      SET @leftover = right(@tmpstr, datalength(@tmpstr) / 2 - @startpos)
   END

   INSERT @tbl(value) VALUES (ltrim(rtrim(@leftover)))
   RETURN
END

ฉันพยายามหลีกเลี่ยงรายการที่คั่นด้วยจุลภาคโดยเฉพาะเพื่อที่ฉันจะได้ไม่ต้องเขียนอะไรแบบนั้น แต่เนื่องจากมันถูกเขียนไปแล้วฉันคิดว่าฉันจะต้องโยนสารละลายกลับเข้าไปในส่วนผสม ;-)
D. Lambert

1
ฉันพูดว่าพยายามแล้วก็จริงง่ายที่สุด คุณสามารถแยกรายการที่คั่นด้วยเครื่องหมายจุลภาคใน C # ในไม่กี่วินาทีของรหัสและคุณสามารถโยนมันลงในฟังก์ชั่นนี้ (หลังจากที่ได้รับมันเข้าไปใน sproc ของคุณ) เร็วพอและคุณไม่ต้องคิดอะไร ~ และฉันรู้ว่าคุณบอกว่าคุณไม่ต้องการใช้ฟังก์ชั่น แต่ฉันคิดว่ามันเป็นวิธีที่ง่ายที่สุด (อาจจะไม่ได้ผลมากที่สุด)
jcolebrand

5

ฉันได้รับชุด 1000s แถวและแถว 10000s ที่ส่งจากแอปพลิเคชันของเราเป็นประจำเพื่อดำเนินการตามขั้นตอนการจัดเก็บ SQL Server ต่างๆ

เพื่อตอบสนองความต้องการด้านประสิทธิภาพเราใช้ TVPs แต่คุณต้องนำนามธรรมของคุณเองของ dbDataReader มาใช้เพื่อเอาชนะปัญหาด้านประสิทธิภาพในโหมดเริ่มต้นของการประมวลผล ฉันจะไม่เข้าไปในคำแนะนำและ whys เนื่องจากพวกเขาอยู่นอกขอบเขตสำหรับคำขอนี้

ฉันไม่ได้พิจารณาการประมวลผล XML เนื่องจากฉันไม่พบการใช้งาน XML ซึ่งยังคงมีประสิทธิภาพมากกว่า 10,000 "แถว"

การประมวลผลรายการสามารถจัดการได้ด้วยการประมวลผลตารางแบบนับส่วนเดียวและสองมิติ (ตัวเลข) เราใช้งานสิ่งเหล่านี้ได้สำเร็จในหลาย ๆ ด้าน แต่ TVP ที่มีการจัดการที่ดีนั้นมีประสิทธิภาพมากกว่าเมื่อมีมากกว่าสองร้อยแถว "

เช่นเดียวกับตัวเลือกทั้งหมดที่เกี่ยวข้องกับการประมวลผล SQL Server คุณต้องเลือกตัวเลือกของคุณตามรูปแบบการใช้งาน


5

ในที่สุดฉันก็มีโอกาสทำ TableValuedParameters และใช้งานได้ดีดังนั้นฉันจะวางโค้ด lotta ทั้งหมดที่แสดงวิธีที่ฉันใช้พวกเขาพร้อมตัวอย่างจากรหัสปัจจุบันของฉัน: (หมายเหตุ: เราใช้ ADO .สุทธิ)

หมายเหตุ: ฉันกำลังเขียนโค้ดบางส่วนสำหรับบริการและฉันมีบิตรหัสที่กำหนดไว้ล่วงหน้าจำนวนมากในคลาสอื่น แต่ฉันเขียนสิ่งนี้เป็นแอปคอนโซลเพื่อให้ฉันสามารถดีบักได้ดังนั้นฉันจึงคัดลอกสิ่งนี้ทั้งหมดจาก แอพคอนโซล แก้ตัวสไตล์การเขียนโค้ดของฉัน (เช่นสตริงการเชื่อมต่อแบบ hardcoded) เนื่องจากเป็น "build หนึ่งเพื่อทิ้ง" ฉันต้องการแสดงให้เห็นว่าฉันใช้List<customObject>และผลักดันมันลงในฐานข้อมูลได้อย่างง่ายดายเหมือนตารางที่ฉันสามารถใช้ในขั้นตอนการจัดเก็บ รหัส C # และ TSQL ด้านล่าง:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using a;

namespace a.EventAMI {
    class Db {
        private static SqlCommand SqlCommandFactory(string sprocName, SqlConnection con) { return new SqlCommand { CommandType = CommandType.StoredProcedure, CommandText = sprocName, CommandTimeout = 0, Connection = con }; }

        public static void Update(List<Current> currents) {
            const string CONSTR = @"just a hardwired connection string while I'm debugging";
            SqlConnection con = new SqlConnection( CONSTR );

            SqlCommand cmd = SqlCommandFactory( "sprocname", con );
            cmd.Parameters.Add( "@CurrentTVP", SqlDbType.Structured ).Value = Converter.GetDataTableFromIEnumerable( currents, typeof( Current ) ); //my custom converter class

            try {
                using ( con ) {
                    con.Open();
                    cmd.ExecuteNonQuery();
                }
            } catch ( Exception ex ) {
                ErrHandler.WriteXML( ex );
                throw;
            }
        }
    }
    class Current {
        public string Identifier { get; set; }
        public string OffTime { get; set; }
        public DateTime Off() {
            return Convert.ToDateTime( OffTime );
        }

        private static SqlCommand SqlCommandFactory(string sprocName, SqlConnection con) { return new SqlCommand { CommandType = CommandType.StoredProcedure, CommandText = sprocName, CommandTimeout = 0, Connection = con }; }

        public static List<Current> GetAll() {
            List<Current> l = new List<Current>();

            const string CONSTR = @"just a hardcoded connection string while I'm debugging";
            SqlConnection con = new SqlConnection( CONSTR );

            SqlCommand cmd = SqlCommandFactory( "sprocname", con );

            try {
                using ( con ) {
                    con.Open();
                    using ( SqlDataReader reader = cmd.ExecuteReader() ) {
                        while ( reader.Read() ) {
                            l.Add(
                                new Current {
                                    Identifier = reader[0].ToString(),
                                    OffTime = reader[1].ToString()
                                } );
                        }
                    }

                }
            } catch ( Exception ex ) {
                ErrHandler.WriteXML( ex );
                throw;
            }

            return l;
        }
    }
}

-------------------
the converter class
-------------------
using System;
using System.Collections;
using System.Data;
using System.Reflection;

namespace a {
    public static class Converter {
        public static DataTable GetDataTableFromIEnumerable(IEnumerable aIEnumerable) {
            return GetDataTableFromIEnumerable( aIEnumerable, null );
        }

        public static DataTable GetDataTableFromIEnumerable(IEnumerable aIEnumerable, Type baseType) {
            DataTable returnTable = new DataTable();

            if ( aIEnumerable != null ) {
                //Creates the table structure looping in the in the first element of the list
                object baseObj = null;

                Type objectType;

                if ( baseType == null ) {
                    foreach ( object obj in aIEnumerable ) {
                        baseObj = obj;
                        break;
                    }

                    objectType = baseObj.GetType();
                } else {
                    objectType = baseType;
                }

                PropertyInfo[] properties = objectType.GetProperties();

                DataColumn col;

                foreach ( PropertyInfo property in properties ) {
                    col = new DataColumn { ColumnName = property.Name };
                    if ( property.PropertyType == typeof( DateTime? ) ) {
                        col.DataType = typeof( DateTime );
                    } else if ( property.PropertyType == typeof( Int32? ) ) {
                        col.DataType = typeof( Int32 );
                    } else {
                        col.DataType = property.PropertyType;
                    }
                    returnTable.Columns.Add( col );
                }

                //Adds the rows to the table

                foreach ( object objItem in aIEnumerable ) {
                    DataRow row = returnTable.NewRow();

                    foreach ( PropertyInfo property in properties ) {
                        Object value = property.GetValue( objItem, null );
                        if ( value != null )
                            row[property.Name] = value;
                        else
                            row[property.Name] = "";
                    }

                    returnTable.Rows.Add( row );
                }
            }
            return returnTable;
        }

    }
}

USE [Database]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROC [dbo].[Event_Update]
    @EventCurrentTVP    Event_CurrentTVP    READONLY
AS

/****************************************************************
    author  cbrand
    date    
    descrip I'll ask you to forgive me the anonymization I've made here, but hope this helps
    caller  such and thus application
****************************************************************/

BEGIN TRAN Event_Update

DECLARE @DEBUG INT

SET @DEBUG = 0 /* test using @DEBUG <> 0 */

/*
    Replace the list of outstanding entries that are still currently disconnected with the list from the file
    This means remove all existing entries (faster to truncate and insert than to delete on a join and insert, yes?)
*/
TRUNCATE TABLE [database].[dbo].[Event_Current]

INSERT INTO [database].[dbo].[Event_Current]
           ([Identifier]
            ,[OffTime])
SELECT [Identifier]
      ,[OffTime]
  FROM @EventCurrentTVP

IF (@@ERROR <> 0 OR @DEBUG <> 0) 
BEGIN
ROLLBACK TRAN Event_Update
END
ELSE
BEGIN
COMMIT TRAN Event_Update
END

USE [Database]
GO

CREATE TYPE [dbo].[Event_CurrentTVP] AS TABLE(
    [Identifier] [varchar](20) NULL,
    [OffTime] [datetime] NULL
)
GO

นอกจากนี้ฉันจะใช้คำวิจารณ์ที่สร้างสรรค์ในรูปแบบการเข้ารหัสของฉันหากคุณมีข้อเสนอ (สำหรับผู้อ่านทุกคนที่พบคำถามนี้) แต่โปรดเก็บไว้อย่างสร้างสรรค์;) ... หากคุณต้องการฉันจริงๆพบฉันในห้องสนทนาที่นี่ . หวังว่าด้วยโค้ดอันนี้จะสามารถดูว่าพวกเขาสามารถใช้List<Current>ตามที่ฉันกำหนดไว้เป็นตารางใน db และ a List<T>ในแอปของพวกเขาได้อย่างไร


3

ฉันอาจจะไปกับข้อเสนอ # 1 หรือสร้างตารางรอยขีดข่วนที่เพิ่งเก็บรหัสประมวลผล แทรกลงในตารางนั้นระหว่างการประมวลผลจากนั้นเมื่อเสร็จแล้วให้เรียก proc ที่คล้ายกับด้านล่าง:

BEGIN TRAN

UPDATE dt
SET processed = 1
FROM dataTable dt
JOIN processedIds pi ON pi.id = dt.id;

TRUNCATE TABLE processedIds

COMMIT TRAN

คุณจะทำเม็ดมีดหลายใบ แต่มันจะอยู่ที่โต๊ะเล็กดังนั้นมันควรจะเร็ว คุณสามารถแบทช์แทรกของคุณโดยใช้ ADO.net หรืออะแดปเตอร์ข้อมูลใดก็ได้ที่คุณใช้


2

ชื่อคำถามรวมถึงภารกิจในการส่งข้อมูลจากแอปพลิเคชันไปยังขั้นตอนการจัดเก็บ ส่วนนั้นถูกตัดออกโดยเนื้อหาของคำถาม แต่ให้ฉันลองตอบคำถามนี้ด้วย

ในบริบทของ SQL เซิร์ฟเวอร์ 2008 ตามที่ระบุโดยแท็กมีอีกบทความดีดีอี Sommarskog อาร์เรย์และรายการใน SQL Server 2008 BTW ฉันพบมันในบทความ Marian ที่อ้างถึงในคำตอบของเขา

แทนที่จะเสนอลิงค์ฉันขออ้างอิงรายการเนื้อหา:

  • บทนำ
  • พื้นหลัง
  • ตารางที่มีค่าพารามิเตอร์ใน T-SQL
  • ผ่านพารามิเตอร์ที่มีมูลค่าตารางจาก ADO .NET
    • ใช้รายการ
    • ใช้ DataTable
    • การใช้ DataReader
    • หมายเหตุสุดท้าย
  • การใช้พารามิเตอร์ที่มีค่าเป็นตารางจาก API อื่น
    • ODBC
    • OLE DB
    • ADO
    • LINQ และ Entity Framework
    • JDBC
    • PHP
    • Perl
    • เกิดอะไรขึ้นถ้า API ของคุณไม่รองรับ TVP
  • ข้อควรพิจารณาด้านประสิทธิภาพ
    • ฝั่งเซิร์ฟเวอร์
    • ด้านลูกค้า
    • คีย์หลักหรือไม่
  • รับทราบและข้อเสนอแนะ
  • ประวัติการแก้ไข

นอกเหนือจากเทคนิคที่กล่าวถึงแล้วฉันรู้สึกว่าในบางกรณีการคัดขนาดใหญ่และการแทรกจำนวนมากควรได้รับการกล่าวถึงในขอบเขตของเคสทั่วไป


1

ผ่านพารามิเตอร์อาร์เรย์ไปยังกระบวนงานที่เก็บไว้

สำหรับ MS SQL 2016 เวอร์ชั่นล่าสุด

ด้วย MS SQL 2016 พวกเขาแนะนำฟังก์ชั่นใหม่: SPLIT_STRING ()เพื่อแยกวิเคราะห์หลายค่า

สิ่งนี้สามารถแก้ปัญหาของคุณได้อย่างง่ายดาย

สำหรับ MS SQL เวอร์ชันที่เก่ากว่า

หากคุณใช้รุ่นเก่ากว่าทำตามขั้นตอนนี้:

ทำให้หนึ่งฟังก์ชั่น:

 ALTER FUNCTION [dbo].[UDF_IDListToTable]
 (
    @list          [varchar](MAX),
    @Seperator     CHAR(1)
  )
 RETURNS @tbl TABLE (ID INT)
 WITH 

 EXECUTE AS CALLER
 AS
  BEGIN
    DECLARE @position INT
    DECLARE @NewLine CHAR(2) 
    DECLARE @no INT
    SET @NewLine = CHAR(13) + CHAR(10)

    IF CHARINDEX(@Seperator, @list) = 0
    BEGIN
    INSERT INTO @tbl
    VALUES
      (
        @list
      )
END
ELSE
BEGIN
    SET @position = 1
    SET @list = @list + @Seperator
    WHILE CHARINDEX(@Seperator, @list, @position) <> 0
    BEGIN
        SELECT @no = SUBSTRING(
                   @list,
                   @position,
                   CHARINDEX(@Seperator, @list, @position) - @position
               )

        IF @no <> ''
            INSERT INTO @tbl
            VALUES
              (
                @no
              )

        SET @position = CHARINDEX(@Seperator, @list, @position) + 1
    END
END
RETURN
END

หลังจากทำสิ่งนี้เพียงแค่ส่งสตริงของคุณไปยังฟังก์ชันนี้ด้วยตัวคั่น

ฉันหวังว่านี่จะเป็นประโยชน์กับคุณ :-)


-1

ใช้สิ่งนี้เพื่อสร้าง "สร้างประเภทตาราง" ตัวอย่างง่าย ๆ สำหรับผู้ใช้

CREATE TYPE unit_list AS TABLE (
    ItemUnitId int,
    Amount float,
    IsPrimaryUnit bit
);

GO
 CREATE TYPE specification_list AS TABLE (
     ItemSpecificationMasterId int,
    ItemSpecificationMasterValue varchar(255)
);

GO
 declare @units unit_list;
 insert into @units (ItemUnitId, Amount, IsPrimaryUnit) 
  values(12,10.50, false), 120,100.50, false), (1200,500.50, true);

 declare @spec specification_list;
  insert into @spec (ItemSpecificationMasterId,temSpecificationMasterValue) 
   values (12,'test'), (124,'testing value');

 exec sp_add_item "mytests", false, @units, @spec


//Procedure definition
CREATE PROCEDURE sp_add_item
(   
    @Name nvarchar(50),
    @IsProduct bit=false,
    @UnitsArray unit_list READONLY,
    @SpecificationsArray specification_list READONLY
)
AS


BEGIN
    SET NOCOUNT OFF     

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