idフィールド(主キー)に基づいてテーブルからいくつかの行を削除するクエリがあります。これは非常に単純なクエリです。
delete all from OUR_TABLE where ID in (123, 345, ...)
問題は、IDの数が膨大になる可能性があることです(例:70k)。そのため、クエリに長い時間がかかります。これを最適化する方法はありますか?(私たちはsybaseを使用しています-それが重要な場合)。
idフィールド(主キー)に基づいてテーブルからいくつかの行を削除するクエリがあります。これは非常に単純なクエリです。
delete all from OUR_TABLE where ID in (123, 345, ...)
問題は、IDの数が膨大になる可能性があることです(例:70k)。そのため、クエリに長い時間がかかります。これを最適化する方法はありますか?(私たちはsybaseを使用しています-それが重要な場合)。
このようなステートメントを実行するには、次の2つの方法があります。
新しいテーブルを作成し、削除する行を除くすべてをコピーします。後でテーブルを入れ替える(alter table name ...
)バカに聞こえても試してみることをお勧めします。一部のデータベースは、削除よりもコピーの方がはるかに高速です。
テーブルを分割します。N個のテーブルを作成し、ビューを使用してそれらを1つに結合します。削除基準でグループ化されたさまざまなテーブルに行を並べ替えます。アイデアは、個々の行を削除するのではなく、テーブル全体を削除することです。
70K個のアイテムを含むIN句を解析することが問題になるのではないかと思います。代わりに結合を使用して一時テーブルを試しましたか?
これをバッチで実行することを検討してください。一度に 1000 件のレコードを実行するループは、すべてを実行する 1 つのクエリよりもはるかに高速である可能性があり、さらに、他のユーザーに対してテーブルを一気にロックアウトしたままにすることもありません。
カスケード削除 (および影響を受ける多くの外部キー テーブル) またはトリガーが関係している場合は、さらに小さなバッチで実行する必要がある場合があります。状況に最適な数を確認するには、実験する必要があります。100 のバッチで削除しなければならなかったテーブルと、50000 のバッチで削除しなければならなかったテーブルがありました (その場合、100 万のレコードを削除していたので幸運でした)。
しかし、いずれにしても、削除するキー値を一時テーブルに入れて、そこから削除します。
パフォーマンスを使い果たしているものを見つけてください!
多くの場合、提供されているソリューションの1つを使用できます。ただし、他にも存在する可能性があります(Oracleの知識に基づいているため、他のデータベースでは状況が異なります。編集:sybaseについて言及したことを確認しました)。
ただし、覚えておいてください。最初にパフォーマンスを使い果たしているものを見つけてください。
DDLステートメントを使用するときは、トランザクションとバックアップに与える可能性のある結果を理解し、受け入れるようにしてください。
SybaseはIN句で70Kの引数を処理できますか?IN
私が使用したすべてのデータベースには、句の引数の数に制限があります。たとえば、Oracleには約1000の制限があります。
IN句の代わりに副選択を作成できますか?これにより、SQLが短縮されます。たぶん、それはIN句のそのような多数の値に役立つ可能性があります。このようなもの:
DELETE FROM OUR_TABLE WHERE ID IN
(SELECT ID FROM somewhere WHERE some_condition)
データベースモデルで許可されている場合は、データベースへの介入によって、多数のレコードの削除を高速化できます。ここにいくつかの戦略があります:
インデックスを削除し、レコードを削除し、インデックスを再作成することで、処理を高速化できます。これにより、レコードの削除中にインデックスツリーのバランスを取り直す必要がなくなります。
テーブルにトリガーがある場合、およびビジネスルールでそれが許可されている場合は、トリガーを無効にします。レコードを削除してから、トリガーを有効にします。
最後に、他の提案どおりに実行します。削除されない行を含むテーブルのコピーを作成してから、元の行を削除し、コピーの名前を変更して、整合性制約がある場合は再作成します。
1、2、3の組み合わせを試してみます。それでもうまくいかない場合は、4です。すべてが遅い場合は、より大きなボックス、つまりより多くのメモリ、より高速なディスクを探します。
また、一時テーブルがおそらく最良の解決策だと思います。
ただし、「delete from .. where ID in(select id from ...)」を実行した場合でも、大規模なクエリでは処理が遅くなる可能性があります。したがって、結合を使用して削除することをお勧めします。多くの人はその機能について知りません。
したがって、このサンプルテーブルを考えると:
-- set up tables for this example
if exists (select id from sysobjects where name = 'OurTable' and type = 'U')
drop table OurTable
go
create table OurTable (ID integer primary key not null)
go
insert into OurTable (ID) values (1)
insert into OurTable (ID) values (2)
insert into OurTable (ID) values (3)
insert into OurTable (ID) values (4)
go
次に、削除コードを次のように記述できます。
create table #IDsToDelete (ID integer not null)
go
insert into #IDsToDelete (ID) values (2)
insert into #IDsToDelete (ID) values (3)
go
-- ... etc ...
-- Now do the delete - notice that we aren't using 'from'
-- in the usual place for this delete
delete OurTable from #IDsToDelete
where OurTable.ID = #IDsToDelete.ID
go
drop table #IDsToDelete
go
-- This returns only items 1 and 4
select * from OurTable order by ID
go
「in」に渡す ID をテーブルと同じ順序で並べ替えてみてください。そうしないと、インデックスが格納されます。これにより、ディスク キャッシュでより多くのヒットが得られる場合があります。
削除する ID を、ID がメイン テーブルと同じ順序でソートされている一時テーブルに配置すると、データベースがメイン テーブルを簡単にスキャンできる場合があります。
データベース サーバー上のすべての CPU を使用するために、複数の接続を使用して接続を介して作業を実行することもできますが、最初にどのロックが解除されるかなどを考えてください。
our_tableには削除カスケードに関する参照がありますか?