次のクエリでは、顧客ごとにトランザクションをカウントする必要があります。 [編集]ただし、トランザクションが1年以上経過している顧客は、結果セットから完全に除外する必要があります。
クエリオプティマイザーは、顧客ごとに1回だけ存在を評価するほどスマートではないでしょうか。
--Count transactions on customers that are less than 1 year old
SELECT t1.CUSTID,COUNT(*)
FROM CUST_TRX t1
WHERE NOT EXISTS (
SELECT FIRST 1 1
FROM CUST_TRX t2
WHERE
t2.CUSTID=t1.CUSTID AND
t2.DATED<CURRENT_DATE-365
GROUP BY t2.CUSTID
)
GROUP BY t1.CUSTID
クエリプランに自然条件はありません。このクエリは、データベースがすべての顧客に対して実行するのではなく、トランザクションごとに存在句を実行しているかのように実行されます。サブクエリでGROUP BYを削除しても、パフォーマンスは同じです。
データベースからより良いパフォーマンスを得ることができるようにこれを行うより良い方法はありますか?可能であれば、CTEを回避して単純な選択クエリが機能することを願っています(他の課題が発生する可能性があります)。基準による他のグループ化のため(ここには表示されていません)、単純にMIN(DATED)をチェックすることはできません。本当に別のクエリを実行する必要があります。
このようなクエリでは、_LEFT OUTER JOIN
_スタイルチェックの代わりに_NOT EXISTS
_スタイルチェックを実行する方が効率的ですこれは、多くの場合、フルインデックススキャン(または正しいインデックスが配置されています)が、メインテーブルに多くの行がある場合、これは、他の場合は結果として生じるであろう大量のインデックスシーク(メインテーブルから返される各行の参照テーブルに1つ)よりも安価です。一部のクエリプランナーは、この同等性を特定し、より適切な選択肢である代替プランを使用することについて非常に優れていますが、これはあなたのケースで起こったように聞こえません。
次のようなものを試してください:
_SELECT t1.CUSTID, COUNT(*)
FROM CUST_TRX t1
LEFT OUTER JOIN
CUST_TRX t2
ON t2.CUSTID=t1.CUSTID
AND t2.DATED<CURRENT_DATE-365
WHERE t2.CUSTID IS NULL
GROUP BY t1.CUSTID
_
(注:私はfirebirdに慣れていないので、上記の構文には微調整が必要かもしれませんが、要点を説明する必要があります)
_WHERE t2.CUSTID IS NULL
_がない場合、_t1
_で一致する_t2
_のすべての行が_t2
_で一致するたびに1回出力され、_t2
_で一致しないものはすべて出力されます1回出力されますが、そのオブジェクトから選択された列はNULLに設定されます。次に、WHERE
句が一致を除外します。
DBエンジンの能力に応じて、特に参照オブジェクト(ここでフィルターを適用した場合の_CUST_TRX
_)のデータ量が膨大な場合、これは大幅に少なくなる可能性があります_WHERE <something> NOT IN
_または_WHERE NOT EXISTS
_オプションよりも効率的であるため、メソッドを使用する前に、まず現実的なデータセットでベンチマークを行ってください。クエリプランナーが_WHERE NOT IN
_の配置がこの方法でより効率的に実行できることに気付かない場合、MS SQL Serverでより効率的に機能することがよくあります。
また、これを行う場合は、コード(および/またはサポートドキュメント)にコメントを残して、より効率的であると期待される_WHERE <something> NOT IN
_または_WHERE NOT EXISTS
_と同等のものとしてこれを実行していることを伝えます。覚えておくと、経験豊富なSQL担当者がパターンを認識しますが、コードを見ている他の人は意図や理由をすぐに理解できず、わかりやすくするために_WHERE NOT EXISTS
_を使用するように戻すことができます。文。
「1年未満の顧客のトランザクションをカウントする」とは、次のことを意味します。
サンプルコードから、私は#1があなたが望むものであることを理解しています。その場合、本当に存在しない場所が本当に必要ですか?次のようなことをしていただけませんか:
SELECT t1.CUSTID, COUNT(*)
FROM CUST_TRX t1
WHERE t1.DATED>=CURRENT_DATE-365
GROUP BY t1.CUSTID
HAVING COUNT(*) > 0
私はFirebirdユーザーではありませんが、GROUP BY/HAVING構文を調べました。
[編集] 1年以上経過したトランザクションを持つ顧客を結果セットから除外します。
OK、これは行を集約して顧客を選択から除外する別の方法です。
SELECT A.CUSTID, A.HowMany
FROM (SELECT t1.CUSTID, COUNT(*) HowMany, MIN(t1.DATED) OldestTran
FROM CUST_TRX t1
GROUP BY t1.CUSTID
HAVING COUNT(*) > 0 AND MIN(t1.DATED) >=CURRENT_DATE-365) AS A
[編集]わかりました。そのため、クエリはより複雑になり、1つのクエリで実現できます。
つまり、最初に投稿したパターンとよく似たパターンを使用する必要があるでしょう。 EXISTSはDISTINCTを意味し、多くの場合、SELECT DISTINCTからのJOINよりも高速であることに注意してください。ただし、さまざまな方法を試して、動作、タイミングなどを比較することができます。次に、最も気に入った方法を選択します。