इसलिए मैंने यह पोस्ट यहां देखा और इसे पढ़ा और ऐसा लगता है कि थोक प्रतिलिपि जाने का तरीका हो सकता है।थोक इसके बारे में सबसे अच्छा तरीका डालने? + मुझे अब तक पूरी तरह से समझने में मदद मिली है जो मुझे अब तक मिली है
What’s the best way to bulk database inserts from c#?
मैं अभी भी कुछ सवाल हैं और पता है कि कैसे चीजें वास्तव में काम करना चाहते हैं।
तो मुझे 2 ट्यूटोरियल मिले।
http://www.codeproject.com/KB/cs/MultipleInsertsIn1dbTrip.aspx#_Toc196622241
http://www.codeproject.com/KB/linq/BulkOperations_LinqToSQL.aspx
सबसे पहले जिस तरह से 2 ado.net 2.0 सुविधाओं का उपयोग करता। थोक Insert और BulkCopy। दूसरा एक एसक्यूएल और ओपनएक्सएमएल के लिए linq का उपयोग करता है।
इस तरह की अपील मुझे है क्योंकि मैं linq का उपयोग पहले से SQL करने के लिए कर रहा हूं और इसे ado.net पर पसंद करता हूं। हालांकि के रूप में व्यक्ति पोस्ट क्या वह सिर्फ (मेरी राय में उस के साथ कुछ भी गलत नहीं) प्रदर्शन की लागत
पहले से समस्या के समाधान के लिए जा रहा मैं पहली बार ट्यूटोरियल में 2 तरीके बारे में बात करेंगे में बताया
मैं, .net 4.0, MVC 2.0, एसक्यूएल सर्वर 2005
- है (ट्यूटोरियल मैं VS2008 और नहीं यकीन है कि क्या .net संस्करण मैं अभी वहाँ नमूना फ़ाइलों को लोड और उन्हें दौड़ा इस्तेमाल किया परीक्षण के लिए) VS2010 एक्सप्रेस उपयोग कर रहा हूँ ado.net 2.0 सबसे वर्तमान संस्करण?
- मैं जिस तकनीक का उपयोग कर रहा हूं उसके आधार पर, क्या मैं कुछ दिखाने के लिए कुछ अपडेट कर रहा हूं जो इसे किसी भी तरह सुधारेंगे?
- क्या कोई बात है कि इन ट्यूटोरियल ने मुझे छोड़ दिया कि मुझे पता होना चाहिए?
BulkInsert
मैं सभी उदाहरण के लिए इस तालिका का उपयोग कर रहा हूँ।
CREATE TABLE [dbo].[TBL_TEST_TEST]
(
ID INT IDENTITY(1,1) PRIMARY KEY,
[NAME] [varchar](50)
)
सपा कोड
USE [Test]
GO
/****** Object: StoredProcedure [dbo].[sp_BatchInsert] Script Date: 05/19/2010 15:12:47 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_BatchInsert] (@Name VARCHAR(50))
AS
BEGIN
INSERT INTO TBL_TEST_TEST VALUES (@Name);
END
सी # कोड
/// <summary>
/// Another ado.net 2.0 way that uses a stored procedure to do a bulk insert.
/// Seems slower then "BatchBulkCopy" way and it crashes when you try to insert 500,000 records in one go.
/// http://www.codeproject.com/KB/cs/MultipleInsertsIn1dbTrip.aspx#_Toc196622241
/// </summary>
private static void BatchInsert()
{
// Get the DataTable with Rows State as RowState.Added
DataTable dtInsertRows = GetDataTable();
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand command = new SqlCommand("sp_BatchInsert", connection);
command.CommandType = CommandType.StoredProcedure;
command.UpdatedRowSource = UpdateRowSource.None;
// Set the Parameter with appropriate Source Column Name
command.Parameters.Add("@Name", SqlDbType.VarChar, 50, dtInsertRows.Columns[0].ColumnName);
SqlDataAdapter adpt = new SqlDataAdapter();
adpt.InsertCommand = command;
// Specify the number of records to be Inserted/Updated in one go. Default is 1.
adpt.UpdateBatchSize = 1000;
connection.Open();
int recordsInserted = adpt.Update(dtInsertRows);
connection.Close();
}
तो पहली बात यह बैच का आकार है। आप बैच आकार को किसी भी चीज़ पर क्यों सेट करेंगे लेकिन रिकॉर्ड की संख्या आप भेज रहे हैं? जैसे कि मैं 500,000 रिकॉर्ड भेज रहा हूं इसलिए मैंने 500,000 का बैच आकार किया।
अगला ऐसा करने पर यह क्रैश क्यों होता है? अगर मैं इसे बैच आकार के लिए 1000 पर सेट करता हूं तो यह ठीक काम करता है।
System.Data.SqlClient.SqlException was unhandled
Message="A transport-level error has occurred when sending the request to the server. (provider: Shared Memory Provider, error: 0 - No process is on the other end of the pipe.)"
Source=".Net SqlClient Data Provider"
ErrorCode=-2146232060
Class=20
LineNumber=0
Number=233
Server=""
State=0
StackTrace:
at System.Data.Common.DbDataAdapter.UpdatedRowStatusErrors(RowUpdatedEventArgs rowUpdatedEvent, BatchCommandInfo[] batchCommands, Int32 commandCount)
at System.Data.Common.DbDataAdapter.UpdatedRowStatus(RowUpdatedEventArgs rowUpdatedEvent, BatchCommandInfo[] batchCommands, Int32 commandCount)
at System.Data.Common.DbDataAdapter.Update(DataRow[] dataRows, DataTableMapping tableMapping)
at System.Data.Common.DbDataAdapter.UpdateFromDataTable(DataTable dataTable, DataTableMapping tableMapping)
at System.Data.Common.DbDataAdapter.Update(DataTable dataTable)
at TestIQueryable.Program.BatchInsert() in C:\Users\a\Downloads\TestIQueryable\TestIQueryable\TestIQueryable\Program.cs:line 124
at TestIQueryable.Program.Main(String[] args) in C:\Users\a\Downloads\TestIQueryable\TestIQueryable\TestIQueryable\Program.cs:line 16
InnerException:
समय यह डालने बैच आकार के साथ 500,000 रिकॉर्ड 1000 की "2 मिनट और 54 सेकंड"
बेशकले लिया डालने के लिए यह कोई आधिकारिक बार मैं एक को रोकने घड़ी (मैं के साथ वहां बैठ गया है ले लिया मुझे यकीन है कि बेहतर तरीके हैं लेकिन यह देखने के लिए बहुत आलसी था कि वे कहां हैं)
तो मुझे लगता है कि मैं अपने सभी अन्य लोगों की तुलना में धीमा धीमा हूं (लिनक को एसक्यूएल डालने की उम्मीद है) और मुझे सच में यकीन नहीं है कि क्यों।
अगला मैं bulkcopy को देखा
/// <summary>
/// An ado.net 2.0 way to mass insert records. This seems to be the fastest.
/// http://www.codeproject.com/KB/cs/MultipleInsertsIn1dbTrip.aspx#_Toc196622241
/// </summary>
private static void BatchBulkCopy()
{
// Get the DataTable
DataTable dtInsertRows = GetDataTable();
using (SqlBulkCopy sbc = new SqlBulkCopy(connectionString, SqlBulkCopyOptions.KeepIdentity))
{
sbc.DestinationTableName = "TBL_TEST_TEST";
// Number of records to be processed in one go
sbc.BatchSize = 500000;
// Map the Source Column from DataTabel to the Destination Columns in SQL Server 2005 Person Table
// sbc.ColumnMappings.Add("ID", "ID");
sbc.ColumnMappings.Add("NAME", "NAME");
// Number of records after which client has to be notified about its status
sbc.NotifyAfter = dtInsertRows.Rows.Count;
// Event that gets fired when NotifyAfter number of records are processed.
sbc.SqlRowsCopied += new SqlRowsCopiedEventHandler(sbc_SqlRowsCopied);
// Finally write to server
sbc.WriteToServer(dtInsertRows);
sbc.Close();
}
}
यह एक वास्तव में तेजी से जाने के लिए लग रहा था और यहां तक कि एक सपा जरूरत नहीं थी (आप थोक प्रति के साथ सपा का उपयोग कर सकते हैं? आप इसे बेहतर होगा कर सकते हैं?)
बैचकोपी को 500,000 बैच आकार के साथ कोई समस्या नहीं थी। तो फिर आप इसे रिकॉर्ड करने की संख्या के बाद छोटे क्यों बनाते हैं?
मैंने पाया कि बैचकॉपी और 500,000 बैच आकार के साथ इसे पूरा करने के लिए केवल 5 सेकंड लिया गया। मैंने फिर 1,000 के बैच आकार के साथ प्रयास किया और इसमें केवल 8 सेकंड लिया गया।
इतनी तेजी से ऊपर बल्बकिन्स एक ऊपर।
अब मैंने अन्य ट्यूटोरियल की कोशिश की।
USE [Test]
GO
/****** Object: StoredProcedure [dbo].[spTEST_InsertXMLTEST_TEST] Script Date: 05/19/2010 15:39:03 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spTEST_InsertXMLTEST_TEST](@UpdatedProdData nText)
AS
DECLARE @hDoc int
exec sp_xml_preparedocument @hDoc OUTPUT,@UpdatedProdData
INSERT INTO TBL_TEST_TEST(NAME)
SELECT XMLProdTable.NAME
FROM OPENXML(@hDoc, 'ArrayOfTBL_TEST_TEST/TBL_TEST_TEST', 2)
WITH (
ID Int,
NAME varchar(100)
) XMLProdTable
EXEC sp_xml_removedocument @hDoc
सी # कोड।
/// <summary>
/// This is using linq to sql to make the table objects.
/// It is then serailzed to to an xml document and sent to a stored proedure
/// that then does a bulk insert(I think with OpenXML)
/// http://www.codeproject.com/KB/linq/BulkOperations_LinqToSQL.aspx
/// </summary>
private static void LinqInsertXMLBatch()
{
using (TestDataContext db = new TestDataContext())
{
TBL_TEST_TEST[] testRecords = new TBL_TEST_TEST[500000];
for (int count = 0; count < 500000; count++)
{
TBL_TEST_TEST testRecord = new TBL_TEST_TEST();
testRecord.NAME = "Name : " + count;
testRecords[count] = testRecord;
}
StringBuilder sBuilder = new StringBuilder();
System.IO.StringWriter sWriter = new System.IO.StringWriter(sBuilder);
XmlSerializer serializer = new XmlSerializer(typeof(TBL_TEST_TEST[]));
serializer.Serialize(sWriter, testRecords);
db.insertTestData(sBuilder.ToString());
}
}
तो मुझे यह पसंद है क्योंकि मुझे ऑब्जेक्ट्स का उपयोग करना पड़ता है, भले ही यह थोड़ी अनावश्यक है। मुझे नहीं लगता कि एसपी कैसे काम करता है। जैसे मुझे पूरी चीज नहीं मिलती है। मुझे नहीं पता कि OPENXML में हुड के नीचे कुछ बैच डालने हैं, लेकिन मुझे यह भी पता नहीं है कि इस उदाहरण को कैसे लेना है और इसे मेरी टेबल में फिट करने के लिए बदलें क्योंकि मैंने कहा कि मुझे नहीं पता कि क्या हो रहा है।
मुझे यह भी नहीं पता कि क्या होगा यदि ऑब्जेक्ट में आपके पास अधिक टेबल हैं। जैसे कि मेरे पास एक उत्पाद नाम तालिका है जो किसी उत्पाद तालिका या उसके जैसा कुछ संबंध है।
linq से sql में आप उत्पाद का नाम ऑब्जेक्ट प्राप्त कर सकते हैं और उसी ऑब्जेक्ट में उत्पाद तालिका में परिवर्तन कर सकते हैं। इसलिए मुझे यकीन नहीं है कि इसे कैसे ध्यान में रखा जाए। मुझे यकीन नहीं है कि मुझे अलग-अलग आवेषण या क्या करना होगा।
समय 500,000 रिकॉर्ड के लिए बहुत अच्छा था यह 52 सेकंड
पिछले रास्ता निश्चित रूप से सिर्फ LINQ उपयोग कर रहा था यह सब करने के लिए ले लिया है और यह बहुत बुरा था।
/// <summary>
/// This is using linq to sql to to insert lots of records.
/// This way is slow as it uses no mass insert.
/// Only tried to insert 50,000 records as I did not want to sit around till it did 500,000 records.
/// http://www.codeproject.com/KB/linq/BulkOperations_LinqToSQL.aspx
/// </summary>
private static void LinqInsertAll()
{
using (TestDataContext db = new TestDataContext())
{
db.CommandTimeout = 600;
for (int count = 0; count < 50000; count++)
{
TBL_TEST_TEST testRecord = new TBL_TEST_TEST();
testRecord.NAME = "Name : " + count;
db.TBL_TEST_TESTs.InsertOnSubmit(testRecord);
}
db.SubmitChanges();
}
}
मैंने केवल 50,000 रिकॉर्ड किए और इसमें एक मिनट लग गए।
तो मैंने वास्तव में इसे linq के साथ थोक डालने के तरीके या थोक प्रतिलिपि को स्कैन करने के लिए सीमित कर दिया। मुझे यकीन नहीं है कि जब आप किसी भी तरह से रिश्ते करते हैं तो इसे कैसे किया जाए। मुझे यकीन नहीं है कि वे दोनों आवेषण के बजाय अपडेट करते समय कैसे खड़े होते हैं क्योंकि मैं अभी तक इसे आजमाने के लिए नहीं मिला हूं।
मुझे नहीं लगता कि मुझे कभी भी एक प्रकार में 50,000 से अधिक रिकॉर्ड डालने/अपडेट करने की आवश्यकता होगी, लेकिन साथ ही मुझे पता है कि मुझे डालने से पहले रिकॉर्ड पर सत्यापन करना होगा ताकि इसे धीमा कर दिया जा सके और उस तरह आपके द्वारा ऑब्जेक्ट्स के रूप में linq को sql nicer बनाता है, खासकर यदि डेटाबेस में डालने से पहले xml फ़ाइल से आपका पहला पार्सिंग डेटा।
पूर्ण सी # कोड
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Data;
using System.Data.SqlClient;
namespace TestIQueryable
{
class Program
{
private static string connectionString = "";
static void Main(string[] args)
{
BatchInsert();
Console.WriteLine("done");
}
/// <summary>
/// This is using linq to sql to to insert lots of records.
/// This way is slow as it uses no mass insert.
/// Only tried to insert 50,000 records as I did not want to sit around till it did 500,000 records.
/// http://www.codeproject.com/KB/linq/BulkOperations_LinqToSQL.aspx
/// </summary>
private static void LinqInsertAll()
{
using (TestDataContext db = new TestDataContext())
{
db.CommandTimeout = 600;
for (int count = 0; count < 50000; count++)
{
TBL_TEST_TEST testRecord = new TBL_TEST_TEST();
testRecord.NAME = "Name : " + count;
db.TBL_TEST_TESTs.InsertOnSubmit(testRecord);
}
db.SubmitChanges();
}
}
/// <summary>
/// This is using linq to sql to make the table objects.
/// It is then serailzed to to an xml document and sent to a stored proedure
/// that then does a bulk insert(I think with OpenXML)
/// http://www.codeproject.com/KB/linq/BulkOperations_LinqToSQL.aspx
/// </summary>
private static void LinqInsertXMLBatch()
{
using (TestDataContext db = new TestDataContext())
{
TBL_TEST_TEST[] testRecords = new TBL_TEST_TEST[500000];
for (int count = 0; count < 500000; count++)
{
TBL_TEST_TEST testRecord = new TBL_TEST_TEST();
testRecord.NAME = "Name : " + count;
testRecords[count] = testRecord;
}
StringBuilder sBuilder = new StringBuilder();
System.IO.StringWriter sWriter = new System.IO.StringWriter(sBuilder);
XmlSerializer serializer = new XmlSerializer(typeof(TBL_TEST_TEST[]));
serializer.Serialize(sWriter, testRecords);
db.insertTestData(sBuilder.ToString());
}
}
/// <summary>
/// An ado.net 2.0 way to mass insert records. This seems to be the fastest.
/// http://www.codeproject.com/KB/cs/MultipleInsertsIn1dbTrip.aspx#_Toc196622241
/// </summary>
private static void BatchBulkCopy()
{
// Get the DataTable
DataTable dtInsertRows = GetDataTable();
using (SqlBulkCopy sbc = new SqlBulkCopy(connectionString, SqlBulkCopyOptions.KeepIdentity))
{
sbc.DestinationTableName = "TBL_TEST_TEST";
// Number of records to be processed in one go
sbc.BatchSize = 500000;
// Map the Source Column from DataTabel to the Destination Columns in SQL Server 2005 Person Table
// sbc.ColumnMappings.Add("ID", "ID");
sbc.ColumnMappings.Add("NAME", "NAME");
// Number of records after which client has to be notified about its status
sbc.NotifyAfter = dtInsertRows.Rows.Count;
// Event that gets fired when NotifyAfter number of records are processed.
sbc.SqlRowsCopied += new SqlRowsCopiedEventHandler(sbc_SqlRowsCopied);
// Finally write to server
sbc.WriteToServer(dtInsertRows);
sbc.Close();
}
}
/// <summary>
/// Another ado.net 2.0 way that uses a stored procedure to do a bulk insert.
/// Seems slower then "BatchBulkCopy" way and it crashes when you try to insert 500,000 records in one go.
/// http://www.codeproject.com/KB/cs/MultipleInsertsIn1dbTrip.aspx#_Toc196622241
/// </summary>
private static void BatchInsert()
{
// Get the DataTable with Rows State as RowState.Added
DataTable dtInsertRows = GetDataTable();
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand command = new SqlCommand("sp_BatchInsert", connection);
command.CommandType = CommandType.StoredProcedure;
command.UpdatedRowSource = UpdateRowSource.None;
// Set the Parameter with appropriate Source Column Name
command.Parameters.Add("@Name", SqlDbType.VarChar, 50, dtInsertRows.Columns[0].ColumnName);
SqlDataAdapter adpt = new SqlDataAdapter();
adpt.InsertCommand = command;
// Specify the number of records to be Inserted/Updated in one go. Default is 1.
adpt.UpdateBatchSize = 500000;
connection.Open();
int recordsInserted = adpt.Update(dtInsertRows);
connection.Close();
}
private static DataTable GetDataTable()
{
// You First need a DataTable and have all the insert values in it
DataTable dtInsertRows = new DataTable();
dtInsertRows.Columns.Add("NAME");
for (int i = 0; i < 500000; i++)
{
DataRow drInsertRow = dtInsertRows.NewRow();
string name = "Name : " + i;
drInsertRow["NAME"] = name;
dtInsertRows.Rows.Add(drInsertRow);
}
return dtInsertRows;
}
static void sbc_SqlRowsCopied(object sender, SqlRowsCopiedEventArgs e)
{
Console.WriteLine("Number of records affected : " + e.RowsCopied.ToString());
}
}
}
तो क्या आपके मन में प्रधान बैच का आकार तो हो सकता है? जहां आप नेटवर्क विलंबता को उतना ही प्रभावित नहीं करते हैं, लेकिन आप 10,000 यात्रा नहीं कर रहे हैं। – chobo2
आप नेटवर्क विलंबता से बचने की कोशिश करना चाहते हैं, यानी जितना संभव हो सके विलंबता के प्रभाव को कम करें। 1000-10000 बैचिंग आमतौर पर ठीक है, लेकिन यह इस बात पर निर्भर करता है कि आपके नेटवर्क में आपके पास कितनी विलंबता है और बैचिंग स्टेटमेंट्स के लिए आप कितनी मेमोरी का उपयोग कर सकते हैं। जैसे यदि आपके पास एन कथन हैं, और एलएमएस की विलंबता है, तो कुल विलंबता एन * एल एमएस है। यदि आप आकार बी में बैच करते हैं, तो विलंबता एन * एल/बी है, इसलिए आप बैच के आकार से विलंबता को कम करते हैं। अपनी स्थिति में स्वीकार्य कुल विलंबता पर पहुंचने के लिए इन चरों को कार्य करें - उदा। क्वेरी निष्पादन समय का एक छोटा प्रतिशत। – mdma
क्या आपका मतलब है कि बैच आकार का लेनदेन लॉग पर कोई प्रभाव नहीं पड़ता है और क्या करता है? –