コメントで次の議論を更新
ここでの問題の原因は、インデックスのカーディナリティが非常に低いことです。IX_Person_Born
SQL インデックスは、値をすばやく絞り込むのに非常に優れていますが、同じ値を持つレコードが多数ある場合は問題があります。
これは、電話帳のインデックスのようなものと考えることができます。「Smith, John」を見つけたい場合、最初に S で始まる名前がたくさんあることがわかり、次に Smith という人のページが次々と表示されます。たくさんのジョン。あなたは本をスキャンすることになります。
電話帳のインデックスがクラスター化されているため、これは複雑になります。レコードは姓でソートされます。代わりに、"John" という名前の全員を見つけたい場合は、多くの検索を行うことになります。
ここには 3,000 万件のレコードがありますが、異なる値は 30 しかありません。つまり、可能な限り最良のインデックスでも約 100 万件のレコードが返されることを意味します。これらの 100 万件の結果のそれぞれは、実際のレコードではありません。これは、インデックスからテーブル (電話帳に例えると、ページ番号) へのルックアップであるため、処理がさらに遅くなります。
年ではなく、カーディナリティ インデックス (完全な生年月日など) の方がはるかに高速です。
これは、すべての OLTP リレーショナル データベースの一般的な問題ですlow cardinality + huge datasets = slow queries
。インデックス ツリーはあまり役に立たないためです。
要するに、T-SQL とインデックスを使用してカウントを取得するための大幅に迅速な方法はありません。
いくつかのオプションがあります。
1. データ集約
OLAP/Cube のロールアップまたは自分で行う:
select Born, count(*)
from Person
group by Born
長所は、キューブの検索やキャッシュのチェックが非常に高速であることです。問題は、データが古くなり、それを説明する何らかの方法が必要になることです。
2. 並列クエリ
2 つのクエリに分割します。
SELECT count(*)
FROM Person
WHERE Born = '1970'
SELECT TOP 30 *
FROM Person
WHERE Born = '1970'
次に、これらを並列サーバー側で実行するか、ユーザー インターフェイスに追加します。
3.ノーSQL
この問題は、SQL を使用しないソリューションが従来のリレーショナル データベースに勝る大きな利点の 1 つです。非 SQL システムでは、Person
テーブルは多数の安価なサーバー間でフェデレーション (またはシャーディング) されます。ユーザーが検索すると、すべてのサーバーが同時にチェックされます。
この時点で、技術的な変更はおそらく出ていますが、調査する価値があるかもしれないので、私はそれを含めました.
私は過去にこの種のサイズのデータベースで同様の問題を抱えており、(コンテキストに応じて) オプション 1 と 2 の両方を使用しました。ここでの合計がページングの場合は、おそらくオプション 2 と AJAX を使用します。呼び出してカウントを取得します。