2016-09-27 6 views
1

私はcsvファイルをSQLサーバーデータベース にインポートする小さなプログラムを持っていますが、ボタンを2回押すか何かを追加すると重複が追加されます。 重複があるとその重複をスキップするように必要です。 誰かがコードを手伝うことができれば、それはすばらしいでしょう。csv import sqlserver c#

EDIT:ボタンが機能している間にボタンを無効にする必要があることに気付きました。これは、1つの問題に対する解決策ですが、データベースに既に存在するものがあれば、その部分をスキップしますデータベースと同じcsvファイルで同じです。

コード:

private void button1_Click(object sender, EventArgs e) 
    { 
     SqlConnection con = new SqlConnection(@"server=localhost;Initial Catalog=klantbestand;Integrated Security=SSPI;"); 
      string filepath = @"C:\clients TEST.csv"; 

     StreamReader sr = new StreamReader(filepath); 

     string line = sr.ReadLine(); 
     string[] value = line.Split(';'); 
     DataTable dt = new DataTable(); 
     DataRow row; 

     foreach (string dc in value) 
     { 
      dt.Columns.Add(new DataColumn(dc)); 
     } 

     while (!sr.EndOfStream) 
     { 
      value = sr.ReadLine().Split(';'); 
      if (value.Length == dt.Columns.Count) 
      { 
       row = dt.NewRow(); 
       row.ItemArray = value; 
       dt.Rows.Add(row); 
      } 
     } 

     SqlBulkCopy bc = new SqlBulkCopy(con.ConnectionString, SqlBulkCopyOptions.TableLock); 
     bc.DestinationTableName = "GegevensCSV"; 
     bc.BatchSize = dt.Rows.Count; 
     con.Open(); 
     bc.WriteToServer(dt); 
     bc.Close(); 
     con.Close(); 
    } 
+0

何らかの種類のミューテックスを作成したり、ボタンが機能している間は無効にするのは安全な選択肢ではないでしょうか? – rualmar

+0

ボタンを2回押すだけではありません。 csvファイルと同じ値がすでにデータベースにある場合です。 – Stijn

答えて

1

@AdaTheDevの回答は正しいです。しかし、これを行うための別の方法があります。

一括インポート中に重複挿入の問題がある場合は、一括コピー機能を使用する代わりにストアドプロシージャを使用して処理することもできます。

2005年以降のバージョンのSQL Serverでは、 "テーブル値のパラメータ"を使用できます。つまり、テーブル全体をパラメータとしてストアドプロシージャに渡してサーバー側で操作できます。

マージコマンド」よりもストアドプロシージャパラメータを使用してサーバー側にテーブルを渡す場合、mergeコマンドはupsertコマンドです。つまり、同じコマンドから必要なレコードを挿入したり更新したり削除したりすることができます。ここで

は、プロセスに関するいくつかの詳細は以下のとおりです。

ステップ1: SQL Serverでテーブル値パラメータとして作成します。コマンドは次のとおりです。ここで

CREATE TYPE [dbo].[TableTypeName] AS TABLE(
    [ColumnName1] [DataType], 
    [ColumnName2] [DataType], 
    [ColumnName3] [DataType] 
) 
GO 

「ColumnName1,2,3」表の列の名前とは「データ型」は、列に割り当てられたSQL Serverのデータ型です。

ステップ2:

CREATE PROCEDURE [dbo].[ProcedureName] 

    @TableTypeName [dbo].[TableTypeName] READONLY 

AS 
BEGIN 

    DECLARE @InsertedRowsId TABLE 
    (
     [InsertedRowId] [DataType] NOT NULL 
    ); 
    DELETE FROM @InsertedRowsId; 

    BEGIN TRY 

     BEGIN TRANSACTION 

      -- Merge command 
      MERGE INTO [dbo].[TableName] AS [Target] 
      USING (  
        SELECT * FROM @TableTypeName        
       ) AS [Source] 
      -- Candidate Keys: All the column(s) combination that makes the record(s) unique. 
      ON [Target].[ColumnName1] = [Source].[ColumnName1] -- Always false, ensures all rows copied 
      AND [Target].[ColumnName2] = [Source].[ColumnName2] 
      AND [Target].[ColumnName2] = [Source].[ColumnName2] 

      WHEN NOT MATCHED THEN 
       INSERT 
        (
         [ColumnName1] 
         ,[ColumnName1] 
         ,[ColumnName1] 
        ) 
       VALUES 
        (
         [Source].[ColumnName1] 
         ,[Source].[ColumnName1] 
         ,[Source].[ColumnName1] 
        );   

     COMMIT TRANSACTION 

    END TRY 
    BEGIN CATCH 
     ROLLBACK TRANSACTION 
    END CATCH 

END 

ステップ3:ストアドプロシージャを呼び出す今最終段階をようmergeコマンドでストアドプロシージャを作成します。

Private void button1_Click(object sender, EventArgs e) 
{ 
    string connectionString = @"server=localhost;Initial Catalog=klantbestand;Integrated Security=SSPI;"; 
    string filepath = @"C:\clients TEST.csv"; 

    StreamReader sr = new StreamReader(filepath); 

    string line = sr.ReadLine(); 
    string[] value = line.Split(';'); 
    DataTable dt = new DataTable(); 
    DataRow row; 

    foreach (string dc in value) 
    { 
     dt.Columns.Add(new DataColumn(dc)); 
    } 

    while (!sr.EndOfStream) 
    { 
     value = sr.ReadLine().Split(';'); 
     if (value.Length == dt.Columns.Count) 
     { 
      row = dt.NewRow(); 
      row.ItemArray = value; 
      dt.Rows.Add(row); 
     } 
    } 

    if (dt.Rows.Count>0) { 
     using (SqlConnection connection = new SqlConnection(connectionString)) { 
     connection.Open(); 
     using (SqlCommand command = connection.CreateCommand()) { 
       command.CommandText = "dbo.ProcedureName"; 
       command.CommandType = CommandType.StoredProcedure; 

       SqlParameter parameter;    
       parameter = command.Parameters.AddWithValue("@TableTypeName", dt);    
       parameter.SqlDbType = SqlDbType.Structured; 
       parameter.TypeName = "dbo.TableTypeName"; 

       command.ExecuteNonQuery(); 
      } 
     } 
    }   
} 

このようにして、重複するレコードを一切持たずにデータを一括インポートすることができます。 必要に応じて、挿入または例外の詳細を記録することもできます。

+0

だから私はこれを試して、私はexeptionを持っているSystem.Data.dllで 'System.Data.SqlClient.SqlException'型の未処理の例外が発生しました 追加情報:文字列またはバイナリデータは切り捨てられます。 テーブル値パラメータ "@TableTypeName"のデータが、パラメータのテーブルタイプに準拠していません。 SQL Serverエラー:8152、状態:10 ステートメントが終了しました。 – Stijn

+0

例外は文字列切り捨てタイプです。つまり、varcharフィールドに定義されたサイズでは不十分です。 \t第2に、FOR "" @TableTypeName "がパラメータのテーブル型に適合しない"ストアドプロシージャで定義されたパラメータ名がSqlParameterで設定されたパラメータ名と一致しないことを示します –

3

Iは、データベーステーブルにバルク・ロードデータに従う一般的な原理は、新しいステージングテーブルを作成することである(すなわちのみインポートの期間、一時的に存在する)、バルクロードすべてそこにデータを格納し、クエリを実行してそのデータを最終的な宛先テーブルに移行します。その後、ステージングテーブルをドロップします。あなたは、その後で物事を実装することができます

  1. あなたがDBにincrease initial bulk loading performance /ターゲットテーブルを使用して他のプロセスに影響を与えることなく、(例えばSqlBulkCopyOptions .TableLockを)最初の競合を減らすことができ
  2. これは、いくつかの利点を提供します最終的な宛先テーブルに既に存在するデータをコピーしないなどの2番目の移行手順。

+0

私はいくつかのコードで私を助けてくれますか?私はプログラミングでまだよく分かっていません。 – Stijn