日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

SQLServer中批量插入数据方式的性能对比 (转)

發(fā)布時(shí)間:2023/12/10 数据库 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SQLServer中批量插入数据方式的性能对比 (转) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)自:http://www.cnblogs.com/wlb/archive/2010/03/02/1676136.html

昨天下午快下班的時(shí)候,無(wú)意中聽(tīng)到公司兩位同事在探討批量向數(shù)據(jù)庫(kù)插入數(shù)據(jù)的性能優(yōu)化問(wèn)題,頓時(shí)來(lái)了興趣,把自己的想法向兩位同事說(shuō)了一下,于是有了本文。

公司技術(shù)背景:數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)類(lèi)(xxx.DataBase.Dll)調(diào)用存儲(chǔ)過(guò)程實(shí)現(xiàn)數(shù)據(jù)庫(kù)的訪(fǎng)問(wèn)。

技術(shù)方案一:

壓縮時(shí)間下程序員寫(xiě)出的第一個(gè)版本,僅僅為了完成任務(wù),沒(méi)有從程序上做任何優(yōu)化,實(shí)現(xiàn)方式是利用數(shù)據(jù)庫(kù)訪(fǎng)問(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)樵矶际且粯拥摹?br />
技術(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è)字段,如下圖所示:

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)型了如下圖所示:

說(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 ONGOSET QUOTED_IDENTIFIER ONGO-- =============================================-- Author:<Kevin>-- Create date: <2010-3-1>-- Description:<創(chuàng)建通行證>-- =============================================Create PROCEDURE [dbo].[CreatePassportWithTVP]
@TVP PassportTableType readonly
ASBEGINSET NOCOUNT ON;
Insert into Passport(PassportKey) select PassportKey from @TVP
END 可能在查詢(xún)分析器中,智能提示會(huì)提示表值類(lèi)型有問(wèn)題,會(huì)出現(xiàn)紅色下劃線(xiàn)(見(jiàn)下圖),不用理會(huì),
繼續(xù)運(yùn)行我們的代碼,完成存儲(chǔ)過(guò)程的創(chuàng)建

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

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

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

通過(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ù)方案。

寫(xiě)到此,本文就算完了,但是對(duì)新技術(shù)的鉆研仍然還在不斷繼續(xù)。要做的東西還是挺多的。

為了方便大家學(xué)習(xí)和交流,代碼文件已經(jīng)打包并上傳了,歡迎共同學(xué)習(xí)探討。
代碼下載 作者:深山老林
出處:http://wlb.cnblogs.com/
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。 Tag標(biāo)簽: kevin,深山老林,SqlBulkCopy,表值參數(shù),Table-Valued Parameter,批量,插入,性能 深山老林
關(guān)注 - 9
粉絲 - 38 榮譽(yù):微軟社區(qū)精英 關(guān)注博主 11 0 0 (請(qǐng)您對(duì)文章做出評(píng)價(jià)) ? 上一篇:再次探擴(kuò)展-對(duì)xVal進(jìn)行擴(kuò)展解決驗(yàn)證不同步的問(wèn)題
? 下一篇:數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)的性能問(wèn)題與瓶頸問(wèn)題

?

轉(zhuǎn)載于:https://www.cnblogs.com/lanru/archive/2010/10/15/1852649.html

總結(jié)

以上是生活随笔為你收集整理的SQLServer中批量插入数据方式的性能对比 (转)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。