20

異なるテーブルに基づく 4 つの異なるレポートを表示する 4 つのタブがあるページがあります。

クエリを使用して各テーブルの行数を取得し、各テーブルで使用select count(*) from <table>可能な行数をタブに表示します。その結果、ページのポストバックごとに 5 つのcount(*)クエリが実行され (カウントを取得するために 4 つ、ページネーションのために 1 つ)、レポート コンテンツを取得するために 1 つのクエリが実行されます。

今私の質問は:count(*)クエリは本当に高価ですか? 複数回クエリするのではなく、ページのビュー状態で行数 (少なくともタブに表示されるもの) を保持する必要がありますか?

COUNT(*) クエリのコストはどれくらいですか?

4

7 に答える 7

11

一般に、コストのCOUNT(*)コストは、クエリ条件を満たすレコードの数と、これらのレコードの準備に必要な時間 (基になるクエリの複雑さに依存します) に比例します。

単一のテーブルを扱っている単純なケースでは、そのような操作を安価にするために、特定の最適化が行われていることがよくあります。たとえば、単一のテーブルから条件COUNT(*)なしで実行すると、メタデータに保存されるため、これは瞬時に行われます。WHEREMyISAMMySQL

たとえば、次の 2 つのクエリを考えてみましょう。

SELECT  COUNT(*)
FROM    largeTableA a

すべてのレコードがクエリを満たすため、COUNT(*)コストはテーブル内のレコード数に比例します (つまり、返されるものに比例します) (行を訪問する必要があり、それを処理するための特定の最適化が行われていないと仮定します)

SELECT  COUNT(*)
FROM    largeTableA a
JOIN    largeTableB b
ON      a.id = b.id

この場合、エンジンはおそらく使用HASH JOINし、実行計画は次のようになります。

  1. 小さい方のテーブルにハッシュ テーブルを作成する
  2. 大きなテーブルをスキャンし、ハッシュ テーブル内の各レコードを検索します
  3. 彼らが行くように一致を数えます。

この場合、COUNT(*)オーバーヘッド (ステップ 3) は無視でき、クエリ時間はステップ 1 と 2、つまりハッシュ テーブルを作成して検索することで完全に定義されます。このようなクエリの場合、時間は次のようになりますO(a + b)。実際には一致の数には依存しません。

a.idただし、との両方にインデックスがある場合はb.idMERGE JOINが選択される可能性があり、COUNT(*)各一致の後にインデックス シークが実行されるため、時間は再び一致の数に比例します。

于 2010-04-27T10:13:57.543 に答える
8

次の前に、 SQL プロファイラーまたはL2SProf のようなアプリ レベルのプロファイラーをアタッチし、コンテキスト内の実際のクエリ コストを確認する必要があります。

  • 問題が何であるかを推測し、潜在的な解決策の利点を判断しようとする

  • da interwebs で他の人が推測できるようにする - このスレッドを含め、引用なしで多くの誤った情報があります (ただし、この投稿にはありません :P)

それができたら、最善のアプローチが何であるかが明確になります。つまり、SELECT COUNT が物事を支配しているかどうかなどです。

そして、それを行うと、選択した変更がプラスまたはマイナスの影響を及ぼしたかどうかもわかります.

于 2010-04-27T10:46:14.373 に答える
3

他の人が言っているようCOUNT(*)に、常に物理的に行をカウントするので、一度それを実行して結果をキャッシュできるのであれば、それは確かに望ましいことです。

ベンチマークを行い、コストがごくわずかであると判断した場合、(現在)問題はありません。

シナリオに対して高すぎることが判明した場合は、「約30,000の1から500を表示する」のように、ページネーションを「あいまい」にすることができます

SELECT rows FROM sysindexes WHERE id = OBJECT_ID('sometable') AND indid < 2

これは、行数の概算を返します(CHECKPOINTまで更新されないため概算です)。

于 2010-04-27T11:05:31.563 に答える
1

ページが遅くなった場合に検討できることの 1 つは、可能な場合はデータベースの往復回数を最小限に抑えることです。クエリが O(1) であってもCOUNT(*)、十分な数のクエリを実行していると、確実に速度が低下する可能性があります。

一度に 5 つの個別のクエリを設定して実行する代わりに、SELECTステートメントを 1 つのバッチで実行し、5 つの結果を一度に処理します。

つまり、ADO.NET を使用している場合は、次のようにします (簡潔にするためにエラー チェックは省略されています。わかりやすくするために、非ループ/非動的です)。

string sql = "SELECT COUNT(*) FROM Table1; SELECT COUNT(*) FROM Table2;"

SqlCommand cmd = new SqlCommand(sql, connection);
SqlDataReader dr = cmd.ExecuteReader();

// Defaults to first result set
dr.Read();
int table1Count = (int)dr[0];

// Move to second result set
dr.NextResult();
dr.Read();
int table2Count = (int)dr[0];

NHibernate などの何らかの ORM を使用している場合は、自動クエリ バッチ処理を有効にする方法が必要です。

于 2010-04-27T12:52:49.663 に答える
0

これは、このテーブルのデータをどのように処理しているかによって異なります。それらが非常に頻繁に変更され、毎回必要な場合は、このテーブルのカウントのみで構成される別のテーブルを埋めるトリガーを作成できます。このデータを個別に表示する必要がある場合は、特定の1つのテーブルに対してのみ「selectcount(*)...」を実行できます。これはすぐに頭に浮かびましたが、これをスピードアップする方法は他にもあると思います。データをキャッシュしますか?:)

于 2010-04-27T11:57:55.383 に答える
0

COUNT(*) can be particularly expensive as it may result in loading (and paging) an entire table, where you may only need a count on a primary key (In some implementations it is optimised).

From the sound of it, you are causing a table load operation each time, which is slow, but unless it is running noticeably slowly, or causing some sort of problem, don't optimise: premature and unnecessary optimisation can cause a great deal of trouble!

A count on an indexed primary key will be much faster, but with the costs of having an index this may provide no benefit.

于 2010-04-27T10:36:13.827 に答える
0

すべての I/O は高価であり、それなしでタスクを達成できるのであれば、そうするべきです。でも必要なら気にしません。

基になるレコードがなくなっているか、追加されているためにそのカウントが間違っている場合にコードの動作が許容される限り、ビューステートにカウントを保存することについて言及していますが、これは確かにオプションです。

于 2010-04-27T10:56:48.663 に答える