42

単一のテーブル (結合なし) から列を選択しようとしていますが、理想的には行の取得を開始する前に、行数のカウントが必要です。必要な情報を提供する 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 for count 発生
  • 別の命令が発生し、行を追加または削除します
  • データの SELECT が発生し、突然配列が間違ったサイズになります。
    -最悪の場合、配列の制限を超えてデータを書き込もうとして、プログラムがクラッシュします。

アプローチ 2 はこの問題を禁止しますか?

また、2 つのアプローチのどちらかが高速になりますか? もしそうなら、どれ?

最後に、考慮すべきより良いアプローチはありますか (おそらく、SQLRowCount を使用して SELECT 結果の行数を返すようにドライバーに指示する方法はありますか?)

質問された方のために、私はネイティブ C++ と前述の SQL ドライバー (Microsoft が提供) を使用しています。

4

10 に答える 10

35

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 の同じ値を持つため、列の値はすべての行で同じになり、等しくなります (inこの場合) これはクエリ内の行数です。

結果セット内の行の総数である各行に列を含める方法のより良い/より単純な例を次に示します。オプションの Partition By 句を削除するだけです。

SELECT my_table.my_col, count(*) OVER() AS 'Count'
  FROM my_table
 WHERE my_table.foo = 'bar'
于 2008-10-28T17:23:28.923 に答える
19

COUNT(*)と実際のクエリで一貫した結果が得られることを 100% 確実にするには、次の 2 つの方法しかありません。

  • アプローチ2のように、クエリと組み合わせましたCOUNT(*)。kogusからのコメントに示されている相関サブクエリフォームではなく、例に示されているフォームをお勧めします。
  • SNAPSHOTまたはSERIALIZABLE分離レベル でトランザクションを開始した後、アプローチ 1 のように 2 つのクエリを使用します。

これらの分離レベルのいずれかを使用することは重要です。他の分離レベルでは、他のクライアントによって作成された新しい行が現在のトランザクションで表示されるようになるためです。詳細については、MSDN のドキュメントをSET TRANSACTION ISOLATION参照してください。

于 2008-10-28T17:21:49.643 に答える
3

アプローチ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';
于 2008-10-28T16:33:44.797 に答える
3

条件に一致する行数がクエリの実行と結果の取得から数ミリ秒で変化する可能性があることが懸念される場合は、トランザクション内でクエリを実行できます/実行する必要があります。

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 を使用して最後のステートメントの影響を受けた行数を取得し、実際のクエリの出力を一時テーブルまたはテーブル変数にリダイレクトして、すべてをまとめて返すことができます。トランザクションの必要はありません:

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
于 2008-10-28T16:57:02.487 に答える
1

行数がselectカウントとselectステートメントの間で変化することを本当に心配している場合は、最初に行を一時テーブルに選択してみませんか?そうすれば、同期することがわかります。

于 2008-10-28T16:26:41.753 に答える
1

ここにいくつかのアイデアがあります:

  • アプローチ#1を使用し、配列のサイズを変更して追加の結果を保持するか、必要に応じて自動的にサイズ変更される型を使用します(使用している言語について言及していないため、より具体的にはできません)。
  • データベースがこれをサポートしている場合、トランザクション内でアプローチ #1 の両方のステートメントを実行して、両方の回数が同じであることを保証できます。
  • データで何をしているのかわかりませんが、最初にすべてを保存せずに結果を処理できる場合は、これが最良の方法かもしれません.
于 2008-10-28T15:50:22.513 に答える
0

このタイプのデータを処理するためのより良いパターンについて考えたいと思うかもしれません。

答えが変わる可能性があるため(独自の問題を引き起こすトランザクションを使用しない限り)、自己予測SQLドライバーは、行を返す前にクエリが返す行数を教えてくれません。

行数は変更されません-GoogleforACIDおよびSQL。

于 2008-10-28T16:14:09.407 に答える
0

結果をベクトルに入れてみませんか? そうすれば、事前にサイズを知る必要はありません。

于 2008-10-28T15:44:19.747 に答える
0
IF (@@ROWCOUNT > 0)
BEGIN
SELECT my_table.my_col
  FROM my_table
 WHERE my_table.foo = 'bar'
END
于 2010-08-02T14:26:00.293 に答える