以下のようなファイル(1,000万レコード)があります。
line1
line2
line3
line4
.......
......
10 million lines
したがって、基本的には1000万レコードをデータベースに挿入したいと思います。ファイルを読み取ってSQL Serverにアップロードします。
C#コード
System.IO.StreamReader file =
new System.IO.StreamReader(@"c:\test.txt");
while((line = file.ReadLine()) != null)
{
// insertion code goes here
//DAL.ExecuteSql("insert into table1 values("+line+")");
}
file.Close();
ただし、挿入には時間がかかります。 C#を使用して、最短の時間で1000万レコードを挿入するにはどうすればよいですか?
更新1:
一括挿入:
BULK INSERT DBNAME.dbo.DATAs
FROM 'F:\dt10000000\dt10000000.txt'
WITH
(
ROWTERMINATOR =' \n'
);
私のテーブルは以下のようなものです:
DATAs
(
DatasField VARCHAR(MAX)
)
しかし、私は次のエラーを得ています:
メッセージ4866、レベル16、状態1、行1
一括読み込みに失敗しました。データファイルの行1、列1の列が長すぎます。フィールドターミネータと行ターミネータが正しく指定されていることを確認してください。メッセージ7399、レベル16、状態1、行1
OLEリンクサーバー "(null)"のDBプロバイダー "BULK"がエラーを報告しました。プロバイダーはエラーに関する情報を提供しませんでした。メッセージ7330、レベル16、状態2、行1
OLEリンクサーバー "(null)"のDBプロバイダー "BULK"から行をフェッチできません。
以下のコードは機能しました:
BULK INSERT DBNAME.dbo.DATAs
FROM 'F:\dt10000000\dt10000000.txt'
WITH
(
FIELDTERMINATOR = '\t',
ROWTERMINATOR = '\n'
);
BulkCopyを介してロードするDataTable
を作成しないようにしてください。これは、より小さなデータセットに対しては問題のないソリューションですが、データベースを呼び出す前に1000万行すべてをメモリにロードする理由はまったくありません。
最善の策(BCP
/_BULK INSERT
_/OPENROWSET(BULK...)
以外)は、テーブル値パラメーター(TVP)を介してファイルからデータベースにコンテンツをストリーミングすることです。 TVPを使用すると、ファイルを開いて行を読み取り、完了するまで行を送信して、ファイルを閉じることができます。このメソッドには、1行のみのメモリフットプリントがあります。 Streaming Data Into SQL Server 2008 From a Application という記事を書きましたが、これはまさにこのシナリオの例です。
構造の簡単な概要は次のとおりです。上記の質問と同じインポートテーブルとフィールド名を想定しています。
必要なデータベースオブジェクト:
_-- First: You need a User-Defined Table Type
CREATE TYPE ImportStructure AS TABLE (Field VARCHAR(MAX));
GO
-- Second: Use the UDTT as an input param to an import proc.
-- Hence "Tabled-Valued Parameter" (TVP)
CREATE PROCEDURE dbo.ImportData (
@ImportTable dbo.ImportStructure READONLY
)
AS
SET NOCOUNT ON;
-- maybe clear out the table first?
TRUNCATE TABLE dbo.DATAs;
INSERT INTO dbo.DATAs (DatasField)
SELECT Field
FROM @ImportTable;
GO
_
上記のSQLオブジェクトを利用するC#アプリのコードは次のとおりです。オブジェクト(DataTableなど)を入力してからストアドプロシージャを実行するのではなく、このメソッドでは、ファイルの内容の読み取りを開始するのがストアドプロシージャの実行であることに注意してください。 Stored Procの入力パラメーターは変数ではありません。これはメソッドGetFileContents
の戻り値です。このメソッドは、SqlCommand
がExecuteNonQuery
を呼び出すときに呼び出され、ファイルを開き、行を読み取り、_IEnumerable<SqlDataRecord>
_および_yield return
_構成を介してSQL Serverに送信します。そして、ファイルを閉じます。ストアドプロシージャはテーブル変数@ImportTableを参照するだけであり、データが到着し始めるとすぐにアクセスできます(注:データは完全ではなくても、短時間持続しますtempdbの内容)。
_using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using Microsoft.SqlServer.Server;
private static IEnumerable<SqlDataRecord> GetFileContents()
{
SqlMetaData[] _TvpSchema = new SqlMetaData[] {
new SqlMetaData("Field", SqlDbType.VarChar, SqlMetaData.Max)
};
SqlDataRecord _DataRecord = new SqlDataRecord(_TvpSchema);
StreamReader _FileReader = null;
try
{
_FileReader = new StreamReader("{filePath}");
// read a row, send a row
while (!_FileReader.EndOfStream)
{
// You shouldn't need to call "_DataRecord = new SqlDataRecord" as
// SQL Server already received the row when "yield return" was called.
// Unlike BCP and BULK INSERT, you have the option here to create a string
// call ReadLine() into the string, do manipulation(s) / validation(s) on
// the string, then pass that string into SetString() or discard if invalid.
_DataRecord.SetString(0, _FileReader.ReadLine());
yield return _DataRecord;
}
}
finally
{
_FileReader.Close();
}
}
_
上記のGetFileContents
メソッドは、以下に示すように、ストアドプロシージャの入力パラメーター値として使用されます。
_public static void test()
{
SqlConnection _Connection = new SqlConnection("{connection string}");
SqlCommand _Command = new SqlCommand("ImportData", _Connection);
_Command.CommandType = CommandType.StoredProcedure;
SqlParameter _TVParam = new SqlParameter();
_TVParam.ParameterName = "@ImportTable";
_TVParam.TypeName = "dbo.ImportStructure";
_TVParam.SqlDbType = SqlDbType.Structured;
_TVParam.Value = GetFileContents(); // return value of the method is streamed data
_Command.Parameters.Add(_TVParam);
try
{
_Connection.Open();
_Command.ExecuteNonQuery();
}
finally
{
_Connection.Close();
}
return;
}
_
その他の注意事項:
SELECT
ステートメントで各レコードの値を操作することもできます。SqlBulkCopy
を超える利点:SqlBulkCopy
はINSERT専用ですが、TVPを使用するとデータを任意の方法で使用できます。MERGE
;を呼び出すことができます。いくつかの条件に基づいてDELETE
を実行できます。データを複数のテーブルに分割できます。等々。ExecuteReader
の代わりにExecuteNonQuery
を呼び出すことで、データベースからデータを取得できます。たとえば、IDENTITY
インポートテーブルにDATAs
フィールドがある場合、OUTPUT
句をINSERT
に追加して、_INSERTED.[ID]
_(ID
がIDENTITY
フィールドの名前であると想定)。または、完全に異なるクエリの結果、または両方を返すことができます。複数の結果セットを送信してReader.NextResult()
経由でアクセスできるためです。 SqlBulkCopy
を使用している場合、データベースから情報を取得することはできませんが、S.O。に関する質問がいくつかあります。 (少なくとも新しく作成されたIDENTITY
値に関して)それを正確に実行したい人々の。C#では、SqlBulkCopy
にファイルを読み取らせることをお勧めします。これを行うには、IDataReader
directをSqlBulkCopy.WriteToServer
メソッドに渡す必要があります。次に例を示します。 http://www.codeproject.com/Articles/228332/IDataReader-implementation-plus-SqlBulkCopy
最良の方法は、1番目のソリューションと2番目のソリューションを組み合わせる方法です。DataTable
を作成し、ループに行を追加してから、BulkCopy
を使用して1つの接続でDBにアップロードします これを使用するには一括コピーのヘルプ
一括コピーは非常にデリケートな操作であるため、dataTableで列名を「テキスト」として宣言し、DBで「テキスト」として列名を宣言すると例外がスローされるなど、注意が必要です。 、 幸運を。