6

500.000 行から 4.000.000 行までをインポートする次のコード (多かれ少なかれ) があります。

$sSql = "Insert into table (a,b,c) VALUES(?,?,?)"
$oSQLStmnt = $pdo->prepare($sSql);
$oSQLStmnt->setAttribute(PDO::SQLSRV_ATTR_ENCODING, PDO::SQLSRV_ENCODING_SYSTEM);
if (!$oSQLStmnt) {
    echo $pdo->errorInfo(); // Handle errors
}
$pdo->beginTransaction();
$iLineCounter = 1;
while (($sLine = fgets ($oCSV, 8000)) !== FALSE) {
      $aLine = explode('|', $sLine); //Fgetscsv did not work properly 
       if ($iLineCounter % 100 == 0) {
            lo("Inserting row " . $iLineCounter);
            $pdo->commit();
            sleep(0.15);
            $pdo->beginTransaction();
       }
       try {
            $oSQLStmnt->execute($aLine);
            $iSuccesulInserts++;
       }
       catch (exception $e) {
            print_r($e);
            $iFailedInserts++;
       }

       $iLineCounter++;
}
$pdo->commit();

ご覧のとおり、100 行ごとにコミットを実行し、スリープも追加しました。以前は 25,000 行ごとに 1 回だけコミットを実行していましたが、スリープは使用しませんでした。しかし、ある時点で、レコードが欠落していることに気付きました。これらの設定(スリープと行数)をいじり始めました。この方法で、欠落しているレコードの数を 50.000 から約 100 に減らしました。しかし、まだ欠落しているレコードがあります。彼らはどこへ行くの?何か問題があるとすぐにエラーを受け取るので、SQL が問題ないことはわかっています。

トランザクション中に多くの挿入をスタックできると思いましたか? beginTransaction の呼び出しが問題になる可能性はありますか?

アップデート:

報奨金は終了し、私はそれを授与しなければなりませんでした。回答ありがとうございます。または実際のヒント、実際に私の質問に答えた人はいませんでした。あなたの提案は大歓迎ですが、私は回避策を求めていませんでした。賞金が授与された答えは、実際に私の質問に答えるのに最も近かったためです。残念ながらうまくいきませんでした。

今のところ、CSV 一括インポートを使用していますが、問題なく動作しますが、この問題を解決するためのヒントが他にある場合はお知らせください。私は自分のオリジナルの方法を使用することを好みます。

4

4 に答える 4

3

私は以前にこの問題を抱えていました。私の場合、SQL ServerがINSERTごとに「1行追加」を返そうとしてメッセージキューがいっぱいになり、エラーを返さずにデータの挿入を停止したため、INSERTの前に「SETNOCOUNTON」を実行する必要がありました。

したがって、INSERTSの前に必ず「SETNOCOUNTON」を実行してみてください。私はそれがあなたの問題を解決するつもりだと思います。

于 2012-07-04T17:43:41.610 に答える
3

sleep () を使用して 0.15 秒実行を遅らせますが、質問: INSERT に 0.15 秒以上かかるとどうなりますか? 実行するスクリプトと、前のコミットが原因でテーブルがブロックされている可能性があります。

次に、データベースで 1 回の実行で複数の INSERT を実行する方法を試してください。次のようなことを試してください:

INSERT INTO example (example_id, name, value, other_value)VALUES
(100, 'Name 1', 'Value 1', 'Other 1'), (101, 'Name 2', 'Value 2', 'Other 2'),
(102, 'Name 3', 'Value 3', 'Other 3'), (103, 'Name 4', 'Value 4', 'Other 4');

これを実現するには、次のようにします。

$sql = ' INSERT INTO example (example_id, name, value, other_value)VALUES';
while (($sLine = fgets ($oCSV, 8000)) !== FALSE) {
    // generate VALUES to INSERT in a $sql .= '(..., ..., ...),'
}

そして走る!

于 2012-07-06T14:40:02.253 に答える
2

@サラティス、

MERGE を使用して目的のアクションを実行する単純な sproc を作成することを検討しましたか? マージはかなりのオーバーヘッドを消費しますが、「マスター」データ ソースから従属データ ソースにレコードを同期するための非常に信頼できる方法であることは常に知っています。

私は、データベースがデータの使用方法を制御する必要があり、コードがデータベースがいつ何をするかを制御する必要があるという哲学を持っています。私が好むのは、データに触れるものはすべてストアド プロシージャに保持し、特定の条件/イベントが発生したときにコードでストアド プロシージャを呼び出すことです。ただし、状況が非常に特殊である可能性があるため、これは正確なベスト プラクティスではありません。

以下のコード スニペットは、マージを実行する方法の例として Microsoft から提供されています。

MERGE Production.UnitMeasure AS target
USING (SELECT @UnitMeasureCode, @Name) AS source (UnitMeasureCode, Name)
ON (target.UnitMeasureCode = source.UnitMeasureCode)
WHEN MATCHED THEN 
    UPDATE SET Name = source.Name
WHEN NOT MATCHED THEN   
    INSERT (UnitMeasureCode, Name)
    VALUES (source.UnitMeasureCode, source.Name)
    OUTPUT deleted.*, $action, inserted.* INTO #MyTempTable;

いくつかの異なるシナリオをカバーする記事全体へのリンクは次のとおりです

ここで、CSV から SQL Server に情報を取得するために、次のリンクで、FROM 句の一部としてファイル パスを使用し、WITH 句で区切り記号を指定して、それを実現する方法について説明します。

BULK INSERT もカバーしていますが、それが最適な場合は、新しいレコードの INSERT と既存のレコードの UPDATES の両方を処理するため、MERGE に部分的です。 http://sqlserverpedia.com/blog/sql-server-bloggers/so-you-want-to-read-csv-files-huh/

参考までに、BULK INSERT は、ファイルが SQL Server インスタンスと同じディスク上にある場合にのみ機能します。私の会社は、SQL Server のローカル ドライブへのアクセスを許可しないので、今夜、自宅でこれをテストして、動作するサンプルを取得する必要があります。

于 2012-07-05T16:45:28.680 に答える
1

挿入ステートメントの代わりにSprocを使用することを検討しましたか?任意の数のレコードを順番に(一度に1つずつ)書き込むことは、時間/エネルギーの無駄です。必要な速度ではありません。

代わりにBULKINSERTまたはXMLを使用して、一度に複数の行を挿入することはできませんか?

于 2012-07-05T10:35:44.343 に答える