溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

SQLServer批量插入數(shù)據(jù)的方式有哪些

發(fā)布時(shí)間:2021-12-20 09:05:24 來(lái)源:億速云 閱讀:420 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容主要講解“SQLServer批量插入數(shù)據(jù)的方式有哪些”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“SQLServer批量插入數(shù)據(jù)的方式有哪些”吧!

技術(shù)方案一:

壓縮時(shí)間下程序員寫(xiě)出的第一個(gè)版本,僅僅為了完成任務(wù),沒(méi)有從程序上做任何優(yōu)化,實(shí)現(xiàn)方式是利用數(shù)據(jù)庫(kù)訪問(wèn)類(lèi)調(diào)用存儲(chǔ)過(guò)程,利用循環(huán)逐條插入。很明顯,這種方式效率并不高,于是有了前面的兩位同事討論效率低的問(wèn)題。

技術(shù)方案二:

由于是考慮到大數(shù)據(jù)量的批量插入,于是我想到了ADO.NET2.0的一個(gè)新的特性:SqlBulkCopy。有關(guān)這個(gè)的性能,很早之前我是親自做過(guò)性能測(cè)試的,效率非常高。這也是我向公司同事推薦的技術(shù)方案。

技術(shù)方案三:

利用SQLServer2008的新特性--表值參數(shù)(Table-Valued Parameter)。表值參數(shù)是SQLServer2008才有的一個(gè)新特性,使用這個(gè)新特性,我們可以把一個(gè)表類(lèi)型作為參數(shù)傳遞到函數(shù)或存儲(chǔ)過(guò)程里。不過(guò),它也有一個(gè)特點(diǎn):表值參數(shù)在插入數(shù)目少于 1000 的行時(shí)具有很好的執(zhí)行性能。

技術(shù)方案四:

對(duì)于單列字段,可以把要插入的數(shù)據(jù)進(jìn)行字符串拼接,最后再在存儲(chǔ)過(guò)程中拆分成數(shù)組,然后逐條插入。查了一下存儲(chǔ)過(guò)程中參數(shù)的字符串的最大長(zhǎng)度,然后除以字段的長(zhǎng)度,算出一個(gè)值,很明顯是可以滿(mǎn)足要求的,只是這種方式跟第一種方式比起來(lái),似乎沒(méi)什么提高,因?yàn)樵矶际且粯拥摹?/p>

技術(shù)方案五:

考慮異步創(chuàng)建、消息隊(duì)列等等。這種方案無(wú)論從設(shè)計(jì)上還是開(kāi)發(fā)上,難度都是有的。

技術(shù)方案一肯定是要被否掉的了,剩下的就是在技術(shù)方案二跟技術(shù)方案三之間做一個(gè)抉擇,鑒于公司目前的情況,技術(shù)方案四跟技術(shù)方案五就先不考慮了。

接下來(lái),為了讓大家對(duì)表值參數(shù)的創(chuàng)建跟調(diào)用有更感性的認(rèn)識(shí),我將寫(xiě)的更詳細(xì)些,文章可能也會(huì)稍長(zhǎng)些,不關(guān)注細(xì)節(jié)的朋友們可以選擇跳躍式的閱讀方式。

再說(shuō)一下測(cè)試方案吧,測(cè)試總共分三組,一組是插入數(shù)量小于1000的,另外兩組是插入數(shù)據(jù)量大于1000的(這里我們分別取10000跟1000000),每組測(cè)試又分10次,取平均值。怎么做都明白了,Let's go!

1.創(chuàng)建表。

為了簡(jiǎn)單,表中只有一個(gè)字段,如下圖所示:

SQLServer批量插入數(shù)據(jù)的方式有哪些

2.創(chuàng)建表值參數(shù)類(lèi)型

我們打開(kāi)查詢(xún)分析器,然后在查詢(xún)分析器中執(zhí)行下列代碼:

Create Type PassportTableType as Table
(
PassportKey nvarchar(50)

)

執(zhí)行成功以后,我們打開(kāi)企業(yè)管理器,按順序依次展開(kāi)下列節(jié)點(diǎn)--數(shù)據(jù)庫(kù)、展開(kāi)可編程性、類(lèi)型、用戶(hù)自定義表類(lèi)型,就可以看到我們創(chuàng)建好的表值類(lèi)型了如下圖所示:

SQLServer批量插入數(shù)據(jù)的方式有哪些

說(shuō)明我們創(chuàng)建表值類(lèi)型成功了。

3.編寫(xiě)存儲(chǔ)過(guò)程

存儲(chǔ)過(guò)程的代碼為:

USE [TestInsert]

GO
/****** Object: StoredProcedure [dbo].[CreatePassportWithTVP] Script Date: 03/02/2010 00:14:45 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:	<Kevin>
-- Create date: <2010-3-1>
-- Description:	<創(chuàng)建通行證>
-- =============================================
Create PROCEDURE [dbo].[CreatePassportWithTVP] 

@TVP PassportTableType readonly

AS
BEGIN
SET NOCOUNT ON;

Insert into Passport(PassportKey) select PassportKey from @TVP

END

可能在查詢(xún)分析器中,智能提示會(huì)提示表值類(lèi)型有問(wèn)題,會(huì)出現(xiàn)紅色下劃線(見(jiàn)下圖),不用理會(huì),繼續(xù)運(yùn)行我們的代碼,完成存儲(chǔ)過(guò)程的創(chuàng)建

SQLServer批量插入數(shù)據(jù)的方式有哪些

4.編寫(xiě)代碼調(diào)用存儲(chǔ)過(guò)程。

三種數(shù)據(jù)庫(kù)的插入方式代碼如下,由于時(shí)間比較緊,代碼可能不那么易讀,特別代碼我加了些注釋。

using System;
using System.Diagnostics;
using System.Data;
using System.Data.SqlClient;
using com.DataAccess;

namespace ConsoleAppInsertTest
{
    class Program
    {
        static string connectionString = SqlHelper.ConnectionStringLocalTransaction;    //數(shù)據(jù)庫(kù)連接字符串
        static int count = 1000000;           //插入的條數(shù)
        static void Main(string[] args)
        {
            //long commonInsertRunTime = CommonInsert();
            //Console.WriteLine(string.Format("普通方式插入{1}條數(shù)據(jù)所用的時(shí)間是{0}毫秒", commonInsertRunTime, count));

            long sqlBulkCopyInsertRunTime = SqlBulkCopyInsert();
            Console.WriteLine(string.Format("使用SqlBulkCopy插入{1}條數(shù)據(jù)所用的時(shí)間是{0}毫秒", sqlBulkCopyInsertRunTime, count));

            long TVPInsertRunTime = TVPInsert();
            Console.WriteLine(string.Format("使用表值方式(TVP)插入{1}條數(shù)據(jù)所用的時(shí)間是{0}毫秒", TVPInsertRunTime, count));
        }

        /// <summary>
        /// 普通調(diào)用存儲(chǔ)過(guò)程插入數(shù)據(jù)
        /// </summary>
        /// <returns></returns>
        private static long CommonInsert()
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            
            string passportKey;
            for (int i = 0; i < count; i++)
            {
                passportKey = Guid.NewGuid().ToString();
                SqlParameter[] sqlParameter = { new SqlParameter("@passport", passportKey) };
                SqlHelper.ExecuteNonQuery(connectionString, CommandType.StoredProcedure, "CreatePassport", sqlParameter);
            }
            stopwatch.Stop();
            return stopwatch.ElapsedMilliseconds;
        }

        /// <summary>
        /// 使用SqlBulkCopy方式插入數(shù)據(jù)
        /// </summary>
        /// <param name="dataTable"></param>
        /// <returns></returns>
        private static long SqlBulkCopyInsert()
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            DataTable dataTable = GetTableSchema();
            string passportKey;
            for (int i = 0; i < count; i++)
            {
                passportKey = Guid.NewGuid().ToString();
                DataRow dataRow = dataTable.NewRow();
                dataRow[0] = passportKey;
                dataTable.Rows.Add(dataRow);
            }

            SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(connectionString);
            sqlBulkCopy.DestinationTableName = "Passport";
            sqlBulkCopy.BatchSize = dataTable.Rows.Count;
            SqlConnection sqlConnection = new SqlConnection(connectionString);
            sqlConnection.Open();
            if (dataTable!=null && dataTable.Rows.Count!=0)
            {
                sqlBulkCopy.WriteToServer(dataTable);
            }
            sqlBulkCopy.Close();
            sqlConnection.Close();

            stopwatch.Stop();
            return stopwatch.ElapsedMilliseconds;
        }

        private static long TVPInsert()
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            DataTable dataTable = GetTableSchema();
            string passportKey;
            for (int i = 0; i < count; i++)
            {
                passportKey = Guid.NewGuid().ToString();
                DataRow dataRow = dataTable.NewRow();
                dataRow[0] = passportKey;
                dataTable.Rows.Add(dataRow);
            }

            SqlParameter[] sqlParameter = { new SqlParameter("@TVP", dataTable) };
            SqlHelper.ExecuteNonQuery(connectionString, CommandType.StoredProcedure, "CreatePassportWithTVP", sqlParameter);

            stopwatch.Stop();
            return stopwatch.ElapsedMilliseconds;
        }

        private static DataTable GetTableSchema()
        {
            DataTable dataTable = new DataTable();
            dataTable.Columns.AddRange(new DataColumn[] { new DataColumn("PassportKey") });
            
            return dataTable;
        }

    }
}

比較神秘的代碼其實(shí)就下面這兩行,該代碼是將一個(gè)dataTable做為參數(shù)傳給了我們的存儲(chǔ)過(guò)程。簡(jiǎn)單吧。

SqlParameter[] sqlParameter = { new SqlParameter("@TVP", dataTable) };

SqlHelper.ExecuteNonQuery(connectionString, CommandType.StoredProcedure, "CreatePassportWithTVP", sqlParameter);

5.測(cè)試并記錄測(cè)試結(jié)果

第一組測(cè)試,插入記錄數(shù)1000

SQLServer批量插入數(shù)據(jù)的方式有哪些

第二組測(cè)試,插入記錄數(shù)10000

SQLServer批量插入數(shù)據(jù)的方式有哪些

第三組測(cè)試,插入記錄數(shù)1000000

SQLServer批量插入數(shù)據(jù)的方式有哪些

通過(guò)以上測(cè)試方案,不難發(fā)現(xiàn),技術(shù)方案二的優(yōu)勢(shì)還是蠻高的。無(wú)論是從通用性還是從性能上考慮,都應(yīng)該是優(yōu)先被選擇的,還有一點(diǎn),它的技術(shù)復(fù)雜度要比技術(shù)方案三要簡(jiǎn)單一些,設(shè)想我們把所有表都創(chuàng)建一遍表值類(lèi)型,工作量還是有的。因此,我依然堅(jiān)持我開(kāi)始時(shí)的決定,向公司推薦使用第二種技術(shù)方案。

到此,相信大家對(duì)“SQLServer批量插入數(shù)據(jù)的方式有哪些”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢(xún),關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI