再帰なしで、関連するCTE
を作成しましたが、要件のビジネスロジックを処理するときに複数の選択が行われます。ただし、行が返されないシナリオに必要なロジックではなく、真ん中のCTE
s選択の1つから見つかったすべての行を返す必要があるとアドバイスされています最終操作の代わりに。
どうすればこれを達成できますか?
次の例は私のプロセスの基本的な解釈であり、問題の操作は複数のカスケードされたCTE選択が本質的に一緒にチェーンされた後に発生します。
例(- SQLFiddle にあります)
ユーザーテーブルには、UserId
sのIDが1
と2
の2つのレコードがあります。最初のCTE
s選択、TargetUsers
は3番目のCTE ComputeWithUsers
がAllUsers
を代わりに使用する必要がある行を返しません。
WITH TargetUsers AS
(
SELECT *
FROM Users
WHERE UserId > 5
)
, AllUsers AS
(
SELECT * FROM Users
)
, ComputeWithUsers
(
-- This CTE will determine it needs to use `AllUsers` and not `TargetUsers`
)
または、メインの操作が行を返さないときにすべてのユーザーを抽出するのに十分なTargetUsers
selectをスマートにする方法はありますか?
これを行う方法は次のとおりです。「1つのクエリですべて実行する」というのは、無意味な人為的な要件ではないと想定しています。
SELECT cols INTO #x FROM dbo.Users WHERE UserID > 5;
IF @@ROWCOUNT > 0
SELECT cols FROM #x;
ELSE
SELECT cols FROM dbo.Users;
「すべてを1つのクエリで実行する」ことが困難な要件であり、#tempテーブルを使用できない場合、または複雑なロジックがあり、私たちからだまされたり、繰り返したくない場合は、パフォーマンスの選択肢にお金を払わなければならないのではないかと思います。これは非常に効率的であるように見えますが、ほとんどの人が期待する/期待するよりも、ベーステーブルへのヒット数が多くなります。
;WITH TargetUsers AS
(
SELECT cols FROM dbo.Users WHERE UserID > 5
),
AllUsers AS
(
SELECT cols FROM dbo.Users
WHERE NOT EXISTS (SELECT 1 FROM TargetUsers)
-- this will only return rows if the first CTE is empty
),
ComputeWithUsers AS
(
SELECT cols FROM TargetUsers
UNION ALL
SELECT cols FROM AllUsers
)
...;
実際、UserIDが主キーであり、フィルターが本当にUserIDにある場合は、次のようにするとよいでしょう(ただし、さまざまな要因によって、計画を確認する必要があります)。
;WITH TargetUserIDs AS
(
SELECT UserID FROM dbo.Users WHERE UserID > 5
),
AllUserIDs AS
(
SELECT UserID FROM dbo.Users
WHERE NOT EXISTS (SELECT 1 FROM TargetUsers)
-- this will only return rows if the first CTE is empty
),
ComputeWithUsers AS
(
SELECT UserID FROM TargetUserIDs
UNION ALL
SELECT UserID FROM AllUserIDs
)
...
-- and join to dbo.Users to get other columns necessary
-- deferring that to as late as possible
うまくいけば、クエリは簡素化のために最適化されていないプラクティスを使用しており、本番環境ではそうではありませんが、これらを読んでください:
パフォーマンスが許容範囲内であると想定すると、最初のフィルターが結果を返せなかった場合にすべてのユーザーを取得する手段として、以下を使用できます。
SELECT *
FROM Users
WHERE UserID > 5
OR NOT EXISTS (SELECT * FROM Users WHERE UserID > 5)
あなたは単に工夫したORを設定しているだけです。これにより、ベース選択が何も返さない場合にすべてのレコードがフィルターを通過できるようになります。