31

これについて同様の質問がいくつか見つかりましたが、私のシナリオに適用する方法がわかりませんでした。

私の関数には@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)

明らかにそれは正しくありません。

正しい構文は何ですか?

4

4 に答える 4

41

EXISTS を使用するようにクエリを変更しました。これは、POST に関連付けられている場所が複数ある場合、削除するために DISTINCT または GROUP BY 句が必要な重複する POST レコードが存在するためです...

サーガブルでないもの

これにより、考えられる解決策の中で最悪の結果が得られます。

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)

sargable の非動的バージョン

自明....

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 Server 2005+):

好むと好まざるとにかかわらず、動的 SQL を使用すると、クエリを 1 回記述できます。SQL Server の EXEC とは異なり、sp_executesql はクエリ プランをキャッシュすることに注意してください。SQL Server で動的 SQL を検討する前に、The Curse and Blessings of Dynamic 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
于 2010-12-20T00:46:58.397 に答える
12

次のように書くことができます

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 はクエリ実行プランを台無しにする可能性があるため、これを行うためのより良い方法があるかもしれません。

ほんの少しなので、ビットに基づいて、ストアド プロシージャで IF を使用したり、コードを呼び出す際に異なるコマンド文字列を使用したりするなど、チェックの有無にかかわらず、SQL の 2 つのブロックを決定する価値があると思われます。

于 2010-12-20T00:30:41.180 に答える
4

CASEステートメントをこれに変更できます。クエリ プランナーはこれを別の方法で認識しますが、OR を使用するよりも効率的ではない可能性があります。

(p.LocationTypeId = CASE @IncludeBelow WHEN 0 THEN p.LocationTypeId ELSE @LocationType END)
于 2014-07-23T15:18:53.177 に答える