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 语句看起来只会像那样。它不会使用多个字段,也不会聚合 - 只是简单的文字输入或输出计算,以更简单的方式向报告编写者公开复杂的过滤条件。
根据经验,我希望索引不会在针对视图的查询中使用。但是,要确定某些内容是否为 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 链接在这里。
最简洁的答案是不。
让我们看看 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)