26

私はC#を使用しており、SqlBulkCopyを使用しています。でも問題があります。あるテーブルに一括挿入してから、別のテーブルに別の一括挿入を行う必要があります。

これら2つにはPK/FKの関係があります。

Table A
Field1 -PK auto incrementing (easy to do SqlBulkCopy as straight forward)

Table B
Field1 -PK/FK - This field makes the relationship and is also the PK of this table. It is not auto incrementing and needs to have the same id as to the row in Table A.

したがって、これらのテーブルには1対1の関係がありますが、テーブルBに必要なため、マスインサートが作成したすべてのPKIDを取得する方法がわかりません。

編集

私はこのようなことをすることができますか?

SELECT * 
FROM Product
WHERE NOT EXISTS (SELECT * FROM ProductReview WHERE Product.ProductId = ProductReview.ProductId AND Product.Qty = NULL AND Product.ProductName != 'Ipad')

これにより、SQLバルクコピーで挿入されたすべての行が検索されます。これから結果を取得し、SPからそれらを使用して一括挿入を実行する方法がわかりません。

これで私が見ることができる唯一の問題は、ユーザーが一度に1つずつレコードを実行していて、このステートメントが同時に実行される場合、「製品レビューテーブル」に行を2回挿入しようとする可能性があることです。

つまり、あるユーザーが手動の方法を使用し、別のユーザーがほぼ同時に大量の方法を使用するようになったとします。

手動の方法。 1.ユーザーがデータを送信します2.Linqto sql製品オブジェクトが作成され、データが入力されて送信されます。3.このオブジェクトにはProductIdが含まれています。4。製品レビューテーブル用に別のlinqtosqlオブジェクトが作成され、挿入されます(手順3の製品IDが一緒に送信されます)。

マスウェイ。 1.ユーザーは、データを共有しているユーザーからデータを取得します。2.共有ユーザーからのすべてのProduct行が取得されます。3.製品行へのSQL一括コピー挿入が発生します。4.私のSPは、Productテーブルにのみ存在し、他のいくつかの条件を満たすすべての行を選択します。5.一括挿入はそれらの行で発生します。

したがって、ステップ3(手動の方法)がステップ4(大量の方法)と同時に発生している場合はどうなりますか。同じ行を2回挿入しようとすると、プライマリ制約が実行されると思います。

4

7 に答える 7

18

そのシナリオでは、ステージングテーブル(つまり、インポートしたいデータのように見えるが、メインのトランザクションテーブルの一部ではないテーブル)にSqlBulkCopy挿入し、DBで/に挿入してデータを移動します。最初の実際のテーブルに。INSERTSELECT

サーバーのバージョンに応じて、2つの選択肢があります。2番目のINSERT/SELECTを2番目の実テーブルに対して実行するか、INSERT/OUTPUT句を使用してテーブルのID行を使用して2番目の挿入を実行することができます。

例えば:

     -- dummy schema
     CREATE TABLE TMP (data varchar(max))
     CREATE TABLE [Table1] (id int not null identity(1,1), data varchar(max))
     CREATE TABLE [Table2] (id int not null identity(1,1), id1 int not null, data varchar(max))

     -- imagine this is the SqlBulkCopy
     INSERT TMP VALUES('abc')
     INSERT TMP VALUES('def')
     INSERT TMP VALUES('ghi')

     -- now push into the real tables
     INSERT [Table1]
     OUTPUT INSERTED.id, INSERTED.data INTO [Table2](id1,data)
     SELECT data FROM TMP
于 2010-05-31T19:34:46.820 に答える
10

アプリで許可されている場合は、一括挿入の識別子(GUIDなど)を格納する別の列を追加できます。このIDを明示的に設定します。

次に、一括挿入後、その識別子を持つ行を選択するだけです。

于 2012-04-30T12:30:52.160 に答える
6

SqlBulkCopyで挿入された行のIDを取得しなければならないという同じ問題がありました。私のID列はID列でした。

解決:

一括コピーで500行以上を挿入し、次のクエリでそれらを選択し直しました。

SELECT TOP InsertedRowCount * 
FROM   MyTable 
ORDER BY ID DESC

このクエリは、挿入したばかりの行とそのIDを返します。私の場合、別のユニークなコラムがありました。そこで、その列とIDを選択しました。次に、次のようなIDictionaryを使用してそれらをマッピングします。

 IDictionary<string, int> mymap = new Dictionary<string, int>()
 mymap[Name] = ID

お役に立てれば。

于 2012-12-14T21:26:56.983 に答える
3

私のアプローチはRiceRiceBabyが説明したものと似ていますが、追加する重要なことの1つは、Max(Id)を取得する呼び出しが、SqlBulkCopy.WriteToServerの呼び出しとともにトランザクションの一部である必要があることです。そうしないと、他の誰かがあなたの取引中に挿入する可能性があり、これはあなたのIDを不正確にするでしょう。これが私のコードです:

public static void BulkInsert<T>(List<ColumnInfo> columnInfo, List<T> data, string 
destinationTableName, SqlConnection conn = null, string idColumn = "Id")
    {
        NLogger logger = new NLogger();

        var closeConn = false;


        if (conn == null)
        {
            closeConn = true;
            conn = new SqlConnection(_connectionString);
            conn.Open();
        }

        SqlTransaction tran = 
    conn.BeginTransaction(System.Data.IsolationLevel.Serializable);

        try
        {
            var options = SqlBulkCopyOptions.KeepIdentity;
            var sbc = new SqlBulkCopy(conn, options, tran);

            var command = new SqlCommand(
                    $"SELECT Max({idColumn}) from {destinationTableName};", conn, 
           tran);
            var id = command.ExecuteScalar();

            int maxId = 0;

            if (id != null && id != DBNull.Value)
            {
                maxId = Convert.ToInt32(id);
            }

            data.ForEach(d =>
                {
                    maxId++;
                    d.GetType().GetProperty(idColumn).SetValue(d, maxId);
                });

            var dt = ConvertToDataTable(columnInfo, data);

            sbc.DestinationTableName = destinationTableName;

            foreach (System.Data.DataColumn dc in dt.Columns)
            {
                sbc.ColumnMappings.Add(dc.ColumnName, dc.ColumnName);
            }

            sbc.WriteToServer(dt);

            tran.Commit();

            if(closeConn)
            {
                conn.Close();
                conn = null;
            }
        }
        catch (Exception ex)
        {
            tran.Rollback();
            logger.Write(LogLevel.Error, $@"An error occurred while performing a bulk 
insert into table {destinationTableName}. The entire
                                           transaction has been rolled back. 

{ex.ToString()}");
            throw ex;
        }
    }
于 2018-10-10T17:37:49.117 に答える
0

ニーズとテーブルの制御の程度によっては、IDENTITY主キーの代わりにUNIQUEIDENTIFIER(Guid)の使用を検討することをお勧めします。これにより、キー管理がデータベースの外に移動し、アプリケーションに移動します。このアプローチにはいくつかの重大なトレードオフがあるため、ニーズを満たさない場合があります。しかし、検討する価値があるかもしれません。一括挿入を介して大量のデータをテーブルに送り込むことが確実である場合は、アプリケーションがデータベースに依存してデータを返すのではなく、オブジェクトモデルでこれらのキーを管理する方が非常に便利です。データ。

前に提案したように、ステージングテーブルを使用したハイブリッドアプローチを採用することもできます。リレーションシップのGUIDを使用してこれらのテーブルにデータを取得し、SQLステートメントを介して整数の外部キーを順番に取得し、データを本番テーブルに送り込むことができます。

于 2010-06-19T22:07:57.243 に答える
0

私は...するだろう:

  1. テーブルでID挿入をオンにします

  2. テーブルの最後の行のIDを取得します

  3. からループ(int i = Id; i < datable.rows.count+1; i++)

  4. ループで、データブルのIdプロパティをに割り当てますi+1

  5. IDの保持をオンにしてSQL一括挿入を実行します。

  6. ID挿入をオフに戻します

これは、アプリケーションが別のスレッドで実行されることによって発生する可能性のあるIDの不一致を防ぐため、SQL一括挿入でIDを取得する最も安全な方法だと思います。

于 2016-02-26T18:22:46.763 に答える
-1

免責事項:私はプロジェクトC#BulkOperationsの所有者です

ライブラリはSqlBulkCopyの制限を克服し、挿入されたID値の出力などの柔軟な機能を追加します。

コードの裏側では、受け入れられた答えとまったく同じように機能しますが、はるかに使いやすくなっています。

var bulk = new BulkOperation(connection);

// Output Identity
bulk.ColumnMappings.Add("ProductID", ColumnMappingDirectionType.Output);
// ... Column Mappings...

bulk.BulkInsert(dt);
于 2016-03-02T22:24:38.537 に答える