web-dev-qa-db-ja.com

なぜバッチの挿入/更新が速いのですか?バッチ更新はどのように機能しますか?

バッチ挿入が高速なのはなぜですか?それは、単一の行を挿入するための接続とセットアップのオーバーヘッドが行のセットで同じだからですか?バッチ挿入を高速化する他の要因は何ですか?

バッチ更新はどのように機能しますか?テーブルに一意性の制約がないと仮定すると、挿入ステートメントは実際にはバッチ内の他の挿入ステートメントに影響を与えません。ただし、バッチ更新中に、更新によってテーブルの状態が変更される可能性があるため、バッチ内の他の更新クエリの結果に影響を与える可能性があります。

バッチ挿入クエリには、1つの大きなクエリにすべての挿入値がある構文があることを知っています。バッチ更新クエリはどのように見えますか?たとえば次の形式の単一の更新クエリがある場合:

update <table> set <column>=<expression> where <condition1>
update <table> set <column>=<expression> where <condition2>
update <table> set <column>=<expression> where <condition3>
update <table> set <column>=<expression> where <condition4>

バッチで使用されるとどうなりますか。単一のクエリはどのようになりますか?

また、バッチの挿入と更新はSQL標準の一部ですか?

37
letronje

バッチ挿入が高速なのはなぜですか?

さまざまな理由により、主な3つは次のとおりです。

  • クエリを再解析する必要はありません。
  • 値は1回のラウンドトリップでサーバーに送信されます
  • コマンドは単一のトランザクション内にあります

それは、単一の行を挿入するための接続とセットアップのオーバーヘッドが行のセットで同じだからですか?

部分的にはい、上記を参照してください。

バッチ更新はどのように機能しますか?

これはRDBMSに依存します。

Oracleでは、すべての値をコレクションとして送信し、このコレクションをJOINのテーブルとして使用できます。

PostgreSQLおよびMySQLでは、次の構文を使用できます。

INSERT
INTO    mytable
VALUES 
        (value1),
        (value2),
        …

クエリを1回準備して、何らかのループで呼び出すこともできます。通常、クライアントライブラリにこれを行う方法があります。

テーブルに一意性の制約がないと仮定すると、挿入ステートメントは実際にはバッチ内の他の挿入ステートメントに影響を与えません。ただし、バッチ更新中に、更新によりテーブルの状態が変更される可能性があるため、バッチ内の他の更新クエリの結果に影響を与える可能性があります。

はい。この動作の恩恵を受ける場合と受けない場合があります。

バッチ挿入クエリには、1つの大きなクエリにすべての挿入値がある構文があることを知っています。バッチ更新クエリはどのように見えますか?

Oracleでは、結合でコレクションを使用します。

MERGE
INTO    mytable
USING   TABLE(:mycol)
ON      …
WHEN MATCHED THEN
UPDATE
SET     …

PostgreSQL

UPDATE  mytable
SET     s.s_start = 1
FROM    (
        VALUES
        (value1),
        (value2),
        …
        ) q
WHERE   …
27
Quassnoi

「バルク/バッチ」アップデートについて、同じテーマの答えを探していました。多くの場合、複数の値セット(「バルク」部分)を含むinsert節と比較することで、問題を説明します。

INSERT INTO mytable (mykey, mytext, myint)
VALUES 
  (1, 'text1', 11),
  (2, 'text2', 22),
  ...

明確な答えはまだ私を避けていましたが、ここで解決策を見つけました: http://www.postgresql.org/docs/9.1/static/sql-values.html

明確にするために:

UPDATE mytable
SET 
  mytext = myvalues.mytext,
  myint = myvalues.myint
FROM (
  VALUES
    (1, 'textA', 99),
    (2, 'textB', 88),
    ...
) AS myvalues (mykey, mytext, myint)
WHERE mytable.mykey = myvalues.mykey

これは、1つのステートメントで多くのデータを含む「バルク」であるという同じ特性を持っています。

26
Qerr

他の投稿では、バルクステートメントが高速である理由と、リテラル値を使用してそれを行う方法について説明しています。

プレースホルダーでそれを行う方法を知ることは重要だと思います。プレースホルダーを使用しないと、巨大なコマンド文字列が発生したり、バグを引用/エスケープしたり、SQLインジェクションを起こしやすいアプリケーションになったりする可能性があります。

PostgreSQL> = 9.1のプレースホルダーを使用した一括挿入

列「col1」、「col2」、および「col3」で構成されるテーブル「mytable」に任意の数の行を挿入するには、すべてが1つになります(1つのステートメント、1つのトランザクション)。

INSERT INTO mytable (col1, col2, col3)
 VALUES (unnest(?), unnest(?), unnest(?))

このステートメントには3つの引数を指定する必要があります。最初の列には、最初の列のすべての値を含める必要があります。したがって、すべての引数は、同じ長さのリスト/ベクター/配列でなければなりません。

PostgreSQL> = 9.1のプレースホルダーによる一括更新

あなたのテーブルは「mytable」と呼ばれているとしましょう。列「キー」と「値」で構成されます。

update mytable 
  set value = data_table.new_value
  from 
    (select unnest(?) as key, unnest(?) as new_value) as data_table
  where mytable.key = data_table.key

これは理解するのが簡単ではないことを知っています。難読化されたSQLのように見えます。反対側:動作し、スケーリングし、文字列の連結なしで動作し、安全で、非常に高速です。

このステートメントには2つの引数を指定する必要があります。最初のものは、列「キー」のすべての値を含むリスト/ベクトル/配列でなければなりません。もちろん、2番目の列には「value」列のすべての値を含める必要があります。

サイズの制限に達した場合は、COPY INTO ... FROM STDIN(PostgreSQL)。

4
hagello

バッチ更新では、データベースは一連のデータに対して機能し、行ごとの更新では、行がある場合と同じコマンドを実行する必要があります。したがって、バッチに100万行を挿入すると、コマンドは1回送信および処理され、行ごとの更新では100万回送信および処理されます。これが、SQL Serverまたは相関サブクエリでカーソルを使用したくない理由でもあります。

sQLサーバーでのセットベースの更新の例:

update mytable
set myfield = 'test'
where myfield is null

これにより、1つのステップでヌルである100万レコードすべてが更新されます。カーソルの更新(非バッチ方式で100万行を更新する方法)は、各行を1つずつ繰り返して更新します。

バッチ挿入の問題は、バッチのサイズです。一度に多くのレコードを更新しようとすると、データベースはプロセス中にテーブルをロックし、他のすべてのユーザーをロックアウトする場合があります。そのため、一度にバッチの一部のみを取得するループを実行する必要がある場合があります(ただし、ほとんどの場合、1行よりも大きい数の方が1行よりも高速になります)。これは、バッチ全体ですが、行ごとの操作よりも高速であり、多くのユーザーが存在し、ユーザーが同じテーブル内の他のレコードを表示および更新しようとしていないダウンタイムがほとんどない実稼働環境で必要になる場合があります。バッチのサイズは、データベース構造と正確に何が起こっているかに大きく依存します(トリガーと多くの制約を持つテーブルは、多くのフィールドを持つテーブルと同じように遅くなり、小さなバッチが必要になります)。

0
HLGEM