web-dev-qa-db-ja.com

CASEステートメントとSARGability-特定のユースケース

編集:SQL Server-これは、バージョンを指定する必要がない一般的な十分な質問であることを願っていますが、私が使用しているほとんどのインスタンスは2012以降です。

私はデータをモックアップして実際にテストするのに十分ではないので、誰かがそれを見て簡単な経験で答えてくれることを願っています。

列にアメリカの州の略語を含む州テーブルがあると想像してください(そして、適切なルックアップ列のように、インデックスが付けられています)。アドホッククエリを作成する場合、ユーザーはこの列にヒットしてフィルターをかけることがよくありますが、データベースで暗黙的ではない情報を表す基準を使用します。

たとえば、「大きな状態」を取得したい場合、アドホッククエリに次のようなフィルタを含めることができます。

...
where
  StateAbbreviation in ('AK', 'TX')

通称恐ろしい「ビジネスルール」

したがって、このクエリは問題なく、パフォーマンスが高く、インデックスを利用します。しかし、「ビッグステート」を照会する必要があるたびに、クマは何を書くべきでしょうか。このフィルターを定義に含めてビューを作成し、ビューを簡単に作成したいと思っています。

ここでの問題は、これらのビジネスルールは、基幹業務をサポートするいくつかのアドホッククエリに固有のものですが、実際には普遍的な用途がないということです。したがって、この方法でデータを除外するビューを作成しても、ほとんど役に立ちません。

そのため、その基準がフィルター内にあるビューを作成する代わりに、次のような基準結果が計算されるビューを作成したいと思います。

select 
  StateAbbreviation
  , IsBig = case when StateAbbreviation in ('AK' , 'TX') then 1 else 0 end
from tblStates

現在、大きな州のクエリを記述したい場合は、

where IsBig = 1

クエリ内。

だから、私の質問は簡単です-その基準でビューが呼び出された場合、StateAbbreviationのインデックスを使用できますか?

CASEステートメントの中で答えを変更する可能性のあるあらゆる種類のことを知っているので、この非常に具体的な質問に答えるために、caseステートメントはそのようにしか見えないものと想定します。複数のフィールドを使用せず、集計も行いません。リテラルの単純なインまたはアウトの計算だけで、複雑なフィルター基準をより簡単な方法でレポート作成者に公開します。

5
liver.larson

経験から、ビューに対するクエリでインデックスが使用されないことを期待します。ただし、SARG可能かどうかを判断するには、インデックスが有益なデータセットに対するクエリの例が必要です。だから、それをテストするのが最善です。

まず、いくつかのテストデータを作成します。

CREATE TABLE dbo.tblStates (
StateAbbreviation VARCHAR(2) NOT NULL,
FLUFF VARCHAR(10) NOT NULL
);

-- insert all possible abbreviations to future-proof table
INSERT INTO dbo.tblStates WITH (TABLOCK)
SELECT CHAR(t1.number) + CHAR(t2.number), REPLICATE('Z', 10)
FROM
(
    SELECT number
    from master..spt_values
    WHERE number BETWEEN 65 and 90
) t1
CROSS JOIN 
(
    SELECT number
    from master..spt_values
    WHERE number BETWEEN 65 and 90
) t2;

CREATE INDEX IX_TBL_STATES ON dbo.tblStates (StateAbbreviation);

このクエリを実行すると、インデックスがシークに使用されていることがわかります。

SELECT StateAbbreviation
FROM dbo.tblStates
where
StateAbbreviation in ('AK', 'TX');

enter image description here

ただし、ビューを作成すると、次のようになります。

CREATE VIEW STATE_VIEW
AS
select 
  StateAbbreviation
  , IsBig = case when StateAbbreviation in ('AK' , 'TX') then 1 else 0 end
from tblStates;

同等のクエリは、インデックスを使用してシークしなくなりました。

SELECT StateAbbreviation
FROM STATE_VIEW
where IsBig = 1;

enter image description here

dbフィドルリンクは here です。

8
Joe Obbish

短い答えはノーです。

Stack OverflowのUsersテーブルを見て、大きな3。

SELECT TOP 3 u.Id
FROM dbo.Users AS u
ORDER BY u.Reputation DESC

これにより、3つのID(22656、29407、157882)が返されます。これらを大きな3つと呼びます。

テーラーメイドのインデックスでビューを作成すると、ビューを満足させるためにインデックスが使用されます。

CREATE VIEW dbo.TopFive
AS 
SELECT TOP 5
        Id, CASE WHEN u.Id IN (22656, 29407, 157882) THEN 1 ELSE 0 END AS IsBig
FROM dbo.Users AS u
ORDER BY u.Reputation DESC
GO 

CREATE NONCLUSTERED INDEX ix_Users_View ON dbo.Users (Reputation DESC, Id)

これは、ビューをクエリすることで簡単に表示できます。

SELECT *
from dbo.TopFive AS tf

Nuts

ここで、その列でフィルターをかけようとすると、計画が変更されます。

SELECT *
from dbo.TopFive AS tf
WHERE tf.IsBig = 1

Nuts

これで、SELECTの直前に追加されるフィルター演算子があります。つまり、インデックスにアクセスしても、行はフィルタリングされません。

Nuts

そのSARGを有効にしたい場合、最善の策は 計算列 を追加してCASE結果を具体化することです。

ALTER TABLE dbo.Users 
ADD IsBig AS CONVERT( BIT, CASE WHEN Id IN (22656, 29407, 157882) THEN 1 ELSE 0 END )

これは、状態のテーブルにかなり単純な追加になるはずです。

8
Erik Darling

残念ながら、このようなビューを常に最適化する必要があります。クエリで使用するトリックは、クエリを重複しない2つのクエリに分割し、UNION ALLを使用して結果をマージすることです。したがって、IsBigの条件が指定されている場合、オプティマイザは1つの結果セットか別の結果セットを選択する必要があります。それ以外の場合はすべてを返します。明らかに、これを維持するのは困難です。

SELECT
  StateAbbreviation,
  1 AS IsBig
FROM
    tblStates
WHERE
    StateAbbreviation in ('AK' , 'TX')
UNION ALL
SELECT
  StateAbbreviation,
  0 AS IsBig
FROM
    tblStates
WHERE
    StateAbbreviation NOT in ('AK' , 'TX');
5
Adán Bucio