ここで同様の質問をいくつか見つけましたが、私のシナリオにどのように適用するかわかりませんでした。
私の関数には@ IncludeBelowというパラメーターがあります。値は0または1(BIT)です。
私はこのクエリを持っています:
SELECT p.*
FROM Locations l
INNER JOIN Posts p
on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND l.SomeOtherCondition = @SomeOtherValue
@IncludeBelowが0の場合、次のようなクエリが必要です。
SELECT p.*
FROM Locations l
INNER JOIN Posts p
on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND l.SomeOtherCondition = @SomeOtherValue
AND p.LocationType = @LocationType -- additional filter to only include level.
@IncludeBelowが1の場合、その最後の行を除外する必要があります。 (つまり、フィルターを適用しません)。
CASE
ステートメントである必要があると思いますが、構文を理解できません。
これが私が試したものです:
SELECT p.*
FROM Locations l
INNER JOIN Posts p
on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND l.SomeOtherCondition = @SomeOtherValue
AND (CASE @IncludeBelow WHEN 0 THEN p.LocationTypeId = @LocationType ELSE 1 = 1)
明らかにそれは正しくありません。
正しい構文は何ですか?
EXISTSを使用するようにクエリを変更しました。POSTに関連付けられている場所が複数ある場合、重複したPOSTレコードが存在するため、DISTINCTまたはGROUP BY句を削除する必要があるためです。 ..
これにより、考えられる最悪のソリューションが実行されます。
SELECT p.*
FROM POSTS p
WHERE EXISTS(SELECT NULL
FROM LOCATIONS l
WHERE l.LocationId = p.LocationId
AND l.Condition1 = @Value1
AND l.SomeOtherCondition = @SomeOtherValue)
AND (@IncludeBelow = 1 OR p.LocationTypeId = @LocationType)
自己説明...
BEGIN
IF @IncludeBelow = 0 THEN
SELECT p.*
FROM POSTS p
WHERE EXISTS(SELECT NULL
FROM LOCATIONS l
WHERE l.LocationId = p.LocationId
AND l.Condition1 = @Value1
AND l.SomeOtherCondition = @SomeOtherValue)
AND p.LocationTypeId = @LocationType
ELSE
SELECT p.*
FROM POSTS p
WHERE EXISTS(SELECT NULL
FROM LOCATIONS l
WHERE l.LocationId = p.LocationId
AND l.Condition1 = @Value1
AND l.SomeOtherCondition = @SomeOtherValue)
END
それが好きか嫌いか、動的SQLではクエリを1回だけ記述できます。 SQL ServerのEXECとは異なり、sp_executesqlはクエリプランをキャッシュすることに注意してください。 SQL Serverで動的SQLを検討する前に、 動的SQLの呪いと祝福 を読むことを強くお勧めします...
DECLARE @SQL VARCHAR(MAX)
SET @SQL = 'SELECT p.*
FROM POSTS p
WHERE EXISTS(SELECT NULL
FROM LOCATIONS l
WHERE l.LocationId = p.LocationId
AND l.Condition1 = @Value1
AND l.SomeOtherCondition = @SomeOtherValue)'
SET @SQL = @SQL + CASE
WHEN @IncludeBelow = 0 THEN
' AND p.LocationTypeId = @LocationType '
ELSE ''
END
BEGIN
EXEC sp_executesql @SQL,
N'@Value1 INT, @SomeOtherValue VARCHAR(40), @LocationType INT',
@Value1, @SomeOtherValue, @LocationType
END
あなたはそれを次のように書くことができます
SELECT p.*
FROM Locations l
INNER JOIN Posts p
ON l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND l.SomeOtherCondition = @SomeOtherValue
AND ((@IncludeBelow = 1) OR (p.LocationTypeId = @LocationType))
これはよく目にするパターンです。オプションの検索パラメータ用。しかし、IIRCはクエリ実行プランを混乱させる可能性があるため、これを行うためのより良い方法があるかもしれません。
これはほんの少しだけなので、チェックの有無にかかわらず、SQLの2つのブロック間で決定する価値があるかもしれません。ストアドプロシージャでIFを使用したり、ビットに基づいて呼び出しコードで異なるコマンド文字列を使用したりしますか?
CASE
ステートメントをこれに変更できます。クエリプランナーはこれを別の方法で認識しますが、ORを使用するよりも効率的ではありません。
(p.LocationTypeId = CASE @IncludeBelow WHEN 0 THEN p.LocationTypeId ELSE @LocationType END)
次のようにsqlステートメントを編集します。
SELECT p.*
FROM Locations l
INNER JOIN Posts p
on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND l.SomeOtherCondition = @SomeOtherValue
AND l.LocationType like @LocationType
@IncludeBelow変数は必要ありません
すべての場所の種類を含めるには、@ LocationType = '%'を設定します。
クエリによって返される場所の種類を制限するにはSet @LocationType = '[A Specific Location Type]'
上記のSetステートメントは、@ LocationType変数が文字データ型であることを前提としています