SELECTステートメントの後に行カウントが必要:最適なSQLアプローチは何ですか?
質問
単一のテーブル(結合なし)から列を選択しようとしていますが、理想的には行の取得を開始する前に、行数のカウントが必要です。必要な情報を提供する2つのアプローチがあります。
アプローチ1:
SELECT COUNT( my_table.my_col ) AS row_count
FROM my_table
WHERE my_table.foo = 'bar'
その後
SELECT my_table.my_col
FROM my_table
WHERE my_table.foo = 'bar'
またはアプローチ2
SELECT my_table.my_col, ( SELECT COUNT ( my_table.my_col )
FROM my_table
WHERE my_table.foo = 'bar' ) AS row_count
FROM my_table
WHERE my_table.foo = 'bar'
これは、SQLドライバー(SQL Native Client 9.0)ではSELECTステートメントでSQLRowCountを使用できないため、情報を割り当てる前に配列を割り当てるために結果の行数を知る必要があるためです。それ。残念ながら、動的に割り当てられたコンテナの使用は、私のプログラムのこの領域ではオプションではありません。
次のシナリオが発生する可能性があることを心配しています:
- カウントのSELECTが発生
- 行を追加または削除する別の命令が発生します
- データのSELECTが発生し、突然配列のサイズが間違っています。
-最悪の場合、これは配列の制限を超えてデータを書き込もうとし、プログラムをクラッシュさせます。
アプローチ2はこの問題を禁止していますか?
また、2つのアプローチのいずれかが高速になりますか?もしそうなら、どれ?
最後に、考慮すべきより良いアプローチがあります(おそらく、SQLRowCountを使用してSELECT結果の行数を返すようにドライバーに指示する方法ですか?)
尋ねた人のために、前述のSQLドライバー(Microsoft提供)でネイティブC ++を使用しています
解決
COUNT(*)
と実際のクエリで一貫した結果が得られることを100%確実にする方法は2つしかありません。
- アプローチ2のように、クエリと
COUNT(*)
を組み合わせました。kogusのコメントに示されている相関サブクエリフォームではなく、例で示すフォームをお勧めします。 - アプローチ1のように、
SNAPSHOT
またはSERIALIZABLE
分離レベルでトランザクションを開始した後、2つのクエリを使用します。
これらの分離レベルのいずれかを使用することは重要です。他の分離レベルを使用すると、他のクライアントが作成した新しい行を現在のトランザクションで表示できるようになるためです。 SET TRANSACTION ISOLATION
のMSDNドキュメントを読んでください。詳細については。
他のヒント
SQL Serverを使用している場合、クエリ後に @@ RowCount 関数(または、結果セットに20億行を超える可能性がある場合、 RowCount_Big()関数)。これは、前のステートメントで選択された行数、または挿入/更新/削除ステートメントの影響を受ける行数を返します。
SELECT my_table.my_col
FROM my_table
WHERE my_table.foo = 'bar'
SELECT @@Rowcount
または、アプローチ#2と同様に送信された結果に行カウントを含める場合は、 OVER句。
SELECT my_table.my_col,
count(*) OVER(PARTITION BY my_table.foo) AS 'Count'
FROM my_table
WHERE my_table.foo = 'bar'
OVER句を使用すると、サブクエリを使用して行数を取得するよりもパフォーマンスが大幅に向上します。 @@ RowCountを使用すると、select @@ RowCountステートメントのクエリコストが発生しないため、最高のパフォーマンスが得られます
コメントへの応答で更新:私が与えた例は、パーティション内の行数を示します-この場合は" PARTITION BY my_table.foo"によって定義されます。各行の列の値は、my_table.fooと同じ値を持つ行の数です。サンプルクエリには句" WHERE my_table.foo = 'bar'"が含まれているため、結果セットのすべての行はmy_table.fooの同じ値を持ち、したがって列の値はすべての行で同じで等しくなります(この場合)これはクエリの行数です。
これは、結果セットの合計行数である列を各行に含める方法のより良い/簡単な例です。オプションのPartition By句を削除するだけです。
SELECT my_table.my_col, count(*) OVER() AS 'Count'
FROM my_table
WHERE my_table.foo = 'bar'
アプローチ2は、常に結果セットに一致するカウントを返します。
カウントの条件がデータセットの条件と一致することを保証するために、サブクエリを外部クエリにリンクすることをお勧めします。
SELECT
mt.my_row,
(SELECT COUNT(mt2.my_row) FROM my_table mt2 WHERE mt2.foo = mt.foo) as cnt
FROM my_table mt
WHERE mt.foo = 'bar';
クエリの実行と結果の取得から数ミリ秒で条件に一致する行の数が変わる可能性がある場合は、トランザクション内でクエリを実行できます/実行する必要があります:
BEGIN TRAN bogus
SELECT COUNT( my_table.my_col ) AS row_count
FROM my_table
WHERE my_table.foo = 'bar'
SELECT my_table.my_col
FROM my_table
WHERE my_table.foo = 'bar'
ROLLBACK TRAN bogus
これにより、常に正しい値が返されます。
さらに、SQL Serverを使用している場合は、@@ ROWCOUNTを使用して、最後のステートメントの影響を受ける行数を取得し、 real クエリの出力を一時テーブルまたはテーブルにリダイレクトできます。変数なので、すべてを返すことができ、トランザクションの必要はありません:
DECLARE @dummy INT
SELECT my_table.my_col
INTO #temp_table
FROM my_table
WHERE my_table.foo = 'bar'
SET @dummy=@@ROWCOUNT
SELECT @dummy, * FROM #temp_table
アイデアは次のとおりです。
- アプローチ#1に進み、追加の結果を保持するために配列のサイズを変更するか、必要に応じて自動的にサイズ変更される型を使用します(使用している言語については言及しないので、より具体的に説明することはできません)。
- データベースがこれをサポートしている場合は、トランザクション内でアプローチ#1の両方のステートメントを実行して、カウントが両方とも同じであることを保証できます。
- データで何をしているのかわかりませんが、最初にすべてを保存せずに結果を処理できる場合、これが最善の方法かもしれません。
選択カウントと選択ステートメントの間で行カウントが変化することを本当に心配している場合は、最初に一時テーブルに行を選択してみませんか?そうすれば、あなたはあなたが同期していることを知っています。
結果をベクターに入れてみませんか?そうすれば、サイズを事前に知る必要はありません。
このタイプのデータを処理するためのより良いパターンを考えてください。
回答を変更する可能性があるため(独自の問題を作成するトランザクションを使用しない限り)、行を返す前にクエリが返す行数を通知する自己予測SQLドライバーはありません。
行数は変更されません-ACIDおよびSQLのGoogle。
IF (@@ROWCOUNT > 0)
BEGIN
SELECT my_table.my_col
FROM my_table
WHERE my_table.foo = 'bar'
END
これは、この質問に対するgoogleの上位結果であるため、追加するだけです。 sqliteでは、これを使用して行カウントを取得しました。
WITH temptable AS
(SELECT one,two
FROM
(SELECT one, two
FROM table3
WHERE dimension=0
UNION ALL SELECT one, two
FROM table2
WHERE dimension=0
UNION ALL SELECT one, two
FROM table1
WHERE dimension=0)
ORDER BY date DESC)
SELECT *
FROM temptable
LEFT JOIN
(SELECT count(*)/7 AS cnt,
0 AS bonus
FROM temptable) counter
WHERE 0 = counter.bonus