1

我写了一个像这样的子查询:

select top 1 A
from my_table
where B = some_joined_value_from_outer_query
order by C desc

我想创建一个非聚集索引来提高这部分的性能。

这里有3个问题:

  1. 索引列的正确顺序是什么?
  2. 此外,A 列应该是索引列还是仅包含在索引中?
  3. 这可以在没有子查询的情况下重写(注意top 1and order by desc)并且可以提高性能吗?

编辑: 这是查询(数据同步过程的一部分):

SELECT ProductId, (
    SELECT Id 
    FROM [Order]
    WHERE Number = (
        SELECT TOP 1 OrderNumber
        FROM OtherDatabase..ReferenceTable
        WHERE ProductNumber = Product.Number
        ORDER BY [Date] DESC)
    ) AS OrderId
FROM Product
4

3 回答 3

4

(一种)

CREATE NONCLUSTERED INDEX foo ON dbo.my_table(B, C DESC) INCLUDE (A);

(乙)

由于 A 不在WHEREorORDER BY中,因此在 d 列的列表中可能就足够了INCLUDE,因为它只是“顺路”而不需要成为键的一部分。

(C)

没有更多上下文无法回答。为什么只包含子查询然后询问我们看不到的外部查询?

编辑关于与@Quassnoi 正在进行的对话,我只是想快速证明非聚集索引中尾随列的排序方向可以对特定查询使用的计划产生很大影响。让我们举一个人为的例子:

CREATE TABLE dbo.foo1(A INT, B INT, C INT);
CREATE NONCLUSTERED INDEX foo1x ON dbo.foo1(B, C) INCLUDE(A);

CREATE TABLE dbo.foo2(A INT, B INT, C INT);
CREATE NONCLUSTERED INDEX foo2x ON dbo.foo2(B, C DESC) INCLUDE(A);

INSERT dbo.foo1 SELECT TOP (500000) c.[object_id], c.[object_id], -1*c.[object_id]
FROM sys.all_columns AS c CROSS JOIN sys.all_objects
ORDER BY c.[object_id];

INSERT dbo.foo2 SELECT TOP (500000) c.[object_id], c.[object_id], -1*c.[object_id]
FROM sys.all_columns AS c CROSS JOIN sys.all_objects
ORDER BY c.[object_id];

现在,让我们运行这两个提议的查询并检查计划:

SELECT  A
FROM    (
        SELECT  A, ROW_NUMBER() OVER (PARTITION BY B ORDER BY C DESC) RN
        FROM    dbo.foo1
        ) q
WHERE   rn = 1;

SELECT  A
FROM    (
        SELECT  A, ROW_NUMBER() OVER (PARTITION BY B ORDER BY C DESC) RN
        FROM    dbo.foo2
        ) q
WHERE   rn = 1;

这是针对 dbo.foo1(其中 C 是 ASC)的查询计划:

在此处输入图像描述

在此处输入图像描述

还有我提到的那种:

在此处输入图像描述


这是针对 dbo.foo2(其中 C 是 DESC)的查询计划:

在此处输入图像描述

在此处输入图像描述


现在,如果您向内部查询添加 WHERE 子句(例如 WHERE B = -1024577103),则计划更加相似。但这也意味着 PARTITION BY 是不必要的,并且需要涉及一些匹配以将外部查询也限制为 B 的值。但是我的观点仍然是,虽然对于问题中的特定查询,索引中每一列的排序方向可能对计划影响不大,但对于所有可以使用相同索引的查询来说,情况并非如此。

于 2012-05-18T15:37:04.790 回答
0

顺序很重要。一阶的列是最重要的。

索引的选择完全基于第一列。仅当索引中列出的第一列在查询中使用时,才考虑使用索引。因此,如果第一列没有匹配,并且在查询的 JOIN、ORDER BY 或 WHERE 子句中使用了该列,则完全忽略索引。

于 2012-05-18T15:38:57.533 回答
0

你可以这样重写它:

SELECT  m.a
FROM    (
        SELECT  m.a, ROW_NUMBER() OVER (PARTITION BY m.b ORDER BY m.c DESC) RN
        FROM    outer_table o
        JOIN    my_table m
        ON      m.b = o.b
        ) q
WHERE   rn = 1

但子查询实际上可能更快(或可能不会)。

在我的博客中查看此条目:

于 2012-05-18T15:41:55.230 回答