CASE 语句和 SARGability - 特定用例

liv*_*son 5 performance index sql-server t-sql execution-plan

编辑:SQL Server - 我希望这是一个足够通用的问题,我不需要指定版本,但我使用的大多数实例都是 2012 或更高版本。

我不够好,无法模拟数据并实际测试它,所以我希望有人可以查看它并以简单的经验回答。

想象一下,您有一个州表,其中的一列包含美国州的缩写(并且已编入索引,就像一个很好的查找列)。在编写临时查询时,用户通常会点击此列进行过滤,但使用的条件表示数据库中未隐含的信息。

例如,如果他们想要获得“大状态”,他们可能会在他们的即席查询中包含一个过滤器,显示类似

...
where
  StateAbbreviation in ('AK', 'TX')
Run Code Online (Sandbox Code Playgroud)

又名可怕的“商业规则”

所以这个查询很好,它执行得很好,并且利用了索引。但是,伙计,每次我们需要查询“大国”时,写下来真是太糟糕了。我很想在它的定义中使用这个过滤器创建一个视图,以使其更容易使用。

这里的问题是,这些业务规则特定于一些支持业务线的即席查询,但实际上并没有普遍用途。因此,创建一个以这种方式过滤数据的视图将没有什么用处。

因此,我想编写一个计算条件结果的视图,而不是编写该条件在过滤器中的视图,例如

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

现在,当他们想为大州写一个查询时,他们只需要包括

where IsBig = 1
Run Code Online (Sandbox Code Playgroud)

在查询中。

所以,我的问题很简单 - 如果使用该条件调用视图,是否可以使用 StateAbbreviation 上的索引?

我知道我可以在 CASE 语句中做各种可能会改变答案的事情,所以为了回答这个非常具体的问题,假设 case 语句看起来只会像那样。它不会使用多个字段,也不会聚合 - 只是简单的文字输入或输出计算,以更简单的方式向报告编写者公开复杂的过滤条件。

Joe*_*ish 8

根据经验,我希望索引不会在针对视图的查询中使用。但是,要确定某些内容是否为 SARGable,您只需要对索引有益的数据集进行示例查询。所以,最好测试一下。

首先,我将创建一些测试数据:

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);
Run Code Online (Sandbox Code Playgroud)

运行此查询时,我们可以看到索引用于执行查找:

SELECT StateAbbreviation
FROM dbo.tblStates
where
StateAbbreviation in ('AK', 'TX');
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

但是,如果我们创建一个视图:

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

等效查询不再使用索引来查找:

SELECT StateAbbreviation
FROM STATE_VIEW
where IsBig = 1;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

db fiddle 链接在这里


Eri*_*ing 8

最简洁的答案是不。

让我们看看 Stack Overflow 中的 Users 表,得到一个大的 3。

SELECT TOP 3 u.Id
FROM dbo.Users AS u
ORDER BY u.Reputation DESC
Run Code Online (Sandbox Code Playgroud)

这给了我们三个 ID:22656、29407、157882。我们称它们为我们的三巨头。

如果我们使用定制的索引创建视图,则索引将用于满足视图。

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)
Run Code Online (Sandbox Code Playgroud)

这很容易通过查询视图来显示。

SELECT *
from dbo.TopFive AS tf
Run Code Online (Sandbox Code Playgroud)

坚果

现在,如果我们尝试对该列进行过滤,计划就会改变。

SELECT *
from dbo.TopFive AS tf
WHERE tf.IsBig = 1
Run Code Online (Sandbox Code Playgroud)

坚果

现在有一个过滤器运算符被添加到SELECT. 这意味着当我们访问索引时不会过滤行。

坚果

如果你想让那个 SARGable,你最好的办法是添加一个计算列来实现CASE结果。

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

这应该是对状态表的一个非常简单的补充。


小智 5

不幸的是,我必须一直优化这样的视图。我用于您的查询的技巧是将它分成两个不重叠的查询并使用 UNION ALL 合并结果。因此,如果指定了 IsBig 条件,优化器将不得不在一个结果集或另一个结果集之间进行选择。否则它返回一切。显然,这更难维持。

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');
Run Code Online (Sandbox Code Playgroud)