実行時に進行状況メッセージを出力したい SQL スクリプトがあります。SQL ステートメント間でメッセージを出力するのは簡単ですが、非常に長時間実行される INSERT INTO SELECT がいくつかあります。たとえば、1000 行ごと、または 5 秒ごとなど、select ステートメントにメッセージを出力させる方法はありますか?
注: これは SQL Anywhere の場合ですが、どの SQL ダイアレクトでの回答でも問題ありません。
実行時に進行状況メッセージを出力したい SQL スクリプトがあります。SQL ステートメント間でメッセージを出力するのは簡単ですが、非常に長時間実行される INSERT INTO SELECT がいくつかあります。たとえば、1000 行ごと、または 5 秒ごとなど、select ステートメントにメッセージを出力させる方法はありますか?
注: これは SQL Anywhere の場合ですが、どの SQL ダイアレクトでの回答でも問題ありません。
単一のクエリの実行ステータスを取得する方法はありません。主流のデータベース エンジンは、この機能を提供していません。
さらに、進行状況の実装が存在する場合、測定可能なオーバーヘッドが生成されるため、進行状況を表示するためにクエリにすでに不快なほど長い時間がかかっている場合、進行状況を表示することでさらにスローダウンを引き起こすことは、設計目標ではない可能性があります。SQL 実行の進行状況の見積もりに関する
この記事は役に立つかもしれませんが、実際的な意味は限られています。
セットベースの操作 (リレーショナル データベースが使用するもの) の進行状況の考え方は、少なくとも進行状況バー (完了した割合と合計) で表示されるほどにはあまり役に立ちません。オプティマイザーが何をする必要があるかを判断し、操作の全コストを実際に理解するまでに、操作の大部分は既に完了しています。進行状況表示は、一連の操作ではなく、反復操作を目的としています。
これは、一般的な SELECT ステートメントの実行について話しています。個別のステートメントである挿入の場合、ステートメントの消費率を監視することにより、サブミッターからそれを行うあらゆる種類の方法があります。それらが一括挿入 (select into、insert from など) である場合は、上で説明したのと同じ問題が実際にあります。セット操作は、プログレス バー タイプの表示をやや無意味にする方法でバッチ処理されます。
SQL 自体には、この種のことに対する規定はありません。これを行うには、データベース エンジンと直接やり取りする必要があり、データベース全体で標準的ではありません。
私は SQL Anywhere エンジン開発チームに所属していますが、現在これを行う方法がありません。約束はできませんが、この種の機能を将来のリリースに追加することを検討しています。
SQLにはこれを直接行う方法がないことに同意します。1 つの方法として、一度に TOP 1000 のみを挿入してから、ステータス メッセージを出力することが考えられます。次に、必要に応じてこれを繰り返します (ある種のループで)。欠点は、自分がどこにいるかを追跡する方法が必要になることです。
このアプローチは、1 つの大きな INSERT を実行するほど効率的ではないことに注意してください。
これに対する SQL 標準のソリューションは確かにありません。絶望的な状況で申し訳ありませんが、Oracle、SQL Server、Sybase、または MySQL でこれを行うことができるものを見たことがないので、SQLAnywhere にはあまり期待できません。
これが私がすることです(Sybase / SQL Server構文):
DECLARE @total_rows int
SELECT @total_rows = count(*)
FROM Source_Table
WHILE @total_rows > (SELECT count(*) FROM Target_Table)
BEGIN
SET rowcount 1000
print 'inserting 1000 rows'
INSERT Target_Table
SELECT *
FROM Source_Table s
WHERE NOT EXISTS( SELECT 1
FROM Target_Table t
WHERE t.id = s.id )
END
set rowcount 0
print 'done'
または、ID に基づいて実行することもできます (ID が数値であると仮定します)。
DECLARE @min_id int,
@max_id int,
@start_id int,
@end_id int
SELECT @min_id = min(id) ,
@max_id = max(id)
FROM Source_Table
SELECT @start_id = @min_id ,
@end_id = @min_id + 1000
WHILE @end_id <= @max_id
BEGIN
print 'inserting id range: ' + convert(varchar,@start_id) + ' to ' + convert(varchar,@end_id)
INSERT Target_Table
SELECT *
FROM Source_Table s
WHERE id BETWEEN @start_id AND @end_id
SELECT @start_id = @end_id + 1,
@end_id = @end_id + 1000
END
set rowcount 0
print 'done'
別の別のプロセスに、挿入が行われているテーブル内の行数をカウントさせて、それらの何パーセントが既に存在するかを判断することも考えられます。もちろん、これには最終的に合計を知る必要があります。これは、サーバーの負荷についてあまり心配していない場合にのみ、おそらく問題ありません。
それが必要な場合、またはあなたが死んだ場合、挿入、更新、削除のために、db変数でいくつかのトリガーロジックを使用でき、SQLを実行して変数データを取得し、ユーザーに進行状況を表示することができます。
使いたくない場合は、例を書いてお送りします。
何か他のものを探して、この古いスレッドに出くわしました。集合演算だからといって進行状況の情報が必要ないという考えには同意しません。ユーザーは、待ち時間がどれくらいかを知っていれば、長い待ち時間でも許容できることがよくあります。
これが私が提案するものです:
これを実行するたびに、挿入された行数と合計時間をログに記録し、そのプロセスの最初にステップを追加して、そのログをクエリし、推定合計時間を計算します。前回の実行に基づいて見積もりを行う場合は、処理が完了するまでの待機時間について、許容できる適切な見積もりを提示できるはずです。
Toad を使用している場合は、テーブルから一連の INSERT ステートメントを生成し、ユーザー入力の頻度でコミットするように構成できます。スクリプトを少し変更して、新しいデータがどれだけコミットされたかを確認できます。
数回の実行のタイミングを計ってから、進行状況バーを平均レコード/秒の速度で進めることで、ユーザーへの影響をシミュレートできます。
唯一の他の方法は
1 - データベース エンジンの API を参照して、そのための準備が整っているかどうかを確認します。
また
2 - INSERT を多くの小さなステートメントに分割し、それらについて報告します。しかし、これはパフォーマンスに重大な悪影響を及ぼします。