2015-10-19 20 views
13

最近、SQLで特定のクエリを実行して複数の異なる方法で実行できる最適な方法を探すために最善を尽くしています。私の研究の中では、WHERE INコンセプトに対する嫌悪感は、それがどのように機能するかという固有の非効率性のために、かなり嫌われてきました。SQL UPDATE WHERE IN(リスト)またはUPDATEはそれぞれ個別に更新されますか?

例えば:私の現在のプロジェクトでWHERE Col IN (val1, val2, val3)

、私はデータの大規模なセットでのUPDATEをやってると、より効率的である次のうちどれ疑問に思って:

(またはより良いオプションが存在するかどうか)
UPDATE table1 SET somecolumn = 'someVal' WHERE ID IN (id1, id2, id3 ....); 

上記のIDのリストは、最大1.5kのIDにすることができます。

VS

コード内のすべてのIDのをループし、それぞれに次のステートメントを実行している:

UPDATE table1 SET somecolumn = 'someVal' WHERE ID = 'theID'; 

自分自身に少ないがありますので、それは、前者はより良い/より高速に動作することをより論理的と思われます実行するクエリ。つまり、SQLの出し入れとクエリのキューイングの仕組みに100%精通しているわけではありません。

また、テーブルロックやその他の一般的なパフォーマンスに関しては、DB上でより親しみがあるとは確信しています。

一般的な情報私はMicrosoft SQL Server 2014を使用しています。プライマリ開発言語はC#です。

すべてのヘルプははるかに高く評価されます。

EDIT:

オプション3:上記で

UPDATE table1 SET somecolumn = 'someVal' WHERE ID IN (SELECT ID FROM @definedTable); 

、@definedTableは内部データがC#で(ASストアドプロシージャへ伝わってくるSQL 'ユーザー定義テーブル型' であります)種類SqlDbType.Structured

IDがどのように入力されているかを尋ねています。 IDはコード内のList<string>にあり、ストアドプロシージャに送信される前にコード内の他のものに使用されます。現在、IDは1つの列(ID)のみを持つ「ユーザー定義の表型」としてストアド・プロシージャに入っています。

私はテーブルにそれらを有するコードを有する大規模な文字列を連結し、ちょうどid1, id2, id3, id4など

+1

実行計画を見てみましたが、どちらが2つのクエリの方が速いのですか? – Japongskie

+1

id1、id2、id3はどこから来ていますか?実際のケースでは、いくつかの条件でフィルタリングした結果、別のテーブルから取得されます。この場合、適切なパフォーマンスを得るために、そのテーブルに参加する方がよいでしょう。 –

+1

どのようにこれらのIDをSQLに渡していますか?あなたのC#コードの値のリストを持っていますか、別のSQLクエリの結果としてそれらを取得しますか? – DavidG

答えて

5

Perlの擬似コードの下に説明するように。

私のストアドプロシージャはtable-valued parameterです。 Use Table-Valued Parametersも参照してください。

UPDATE table1 SET somecolumn = 'someVal' WHERE ID IN (SELECT ID FROM @definedTable); 

は、1500回を超え、一度プロシージャを呼び出す方が良いです:あなたが言ったように

手順では、1つの文で、無ループは、そこにあります。 1,500トランザクションよりも1つのトランザクションを持つ方が良いです。

@definedTableの行数が10Kを超える場合は、10Kのバッチで分割することを検討します。


this answerに示すように、あなたの第一の変形がIN句でいくつかの値のためにOKですが、あなたが本当に高い数字(60K +)に到達したとき、あなたがこのような何かを見ることができます:

メッセージ8623、レベル16、状態1、行1クエリプロセッサに の内部リソースがなくなり、クエリプランを生成できませんでした。これはまれな イベントです。非常に複雑なクエリまたはクエリの場合は、 が非常に多数のテーブルまたはパーティションを参照することが予想されます。クエリ を簡略化してください。このメッセージが誤って受信されたと思われる場合は、 カスタマーサポートサービスに連絡してください。

1

のように見えるあなたは間違いなくループを使用して送ってはいけません変数としてSPにそれを吐きよりも良いかもしれないと思いました各IDの新しいSQL文全体。その場合、SQLエンジンはSQL文を再コンパイルし、毎回実行計画などを立てなければなりません。

おそらく最も良いことは、プレースホルダを備えたプリペアドステートメントを作成し、各値のステートメントを実行するデータをループすることです。ステートメントはデータベースエンジンのメモリにとどまり、ゼロから開始するのではなく、新しい値ですぐに呼び出します。

大規模なデータベースがあり、これを頻繁に実行する場合は、そのID値にインデックスを作成してください。そうしないと、すべての値で完全なテーブルスキャンを実行する必要があります。

編集:私はあなたの番目のオプションを使用していますし、それは素晴らしい作品

#!/usr/bin/perl 
use DBI; 
$dbh = DBI->connect('dbi:Oracle:MY_DB', 'scott', 'tiger', { RaiseError => 1, PrintError =>1, AutoCommit => 0 }); 
$sth = $dbh->prepare ("UPDATE table1 SET somecolumn = ? WHERE id = ?"); 
foreach $tuple (@updatetuples) { 
    $sth->execute($$tuple[1], $$tuple[0]); 
} 
$dbh->commit; 
$sth->finish; 
$dbh->disconnect; 
exit (0); 
+0

データがプログラムから来ていると思っていました。別のテーブル。 ITは完全に明確ではありません。しかし、その場合、私はまだこれが最善のルートだと思っています。私はC#を知らないので、ここでいくつかのテストされていないPerl擬似コードがあります:ああ、私はそれを答えに入れなければならないと思います。 – Laserbeak

2

あなたの第1または第3のオプションは、最善の方法です。どちらの場合でも、インデックスはtable1(id)になります。

通常、複数のクエリではなく1つのクエリを実行するほうが、データベースのデータを出入りするオーバーヘッドが増加するためです。さらに、各更新はトランザクションを開始し、それをコミットします。オーバーヘッドが増えます。つまり、何千ものレコードを更新しない限り、これはおそらく重要ではありません。通常のシステムでは、オーバーヘッドは数百マイクロ秒またはミリ秒単位で測定されます。

関連する問題