2

我正在从约 3.5 亿条记录的表中选择结果,它的运行速度非常慢 - 大约 10 分钟。罪魁祸首似乎是ORDER BY,好像我删除了它,查询只需要一点时间。这是要点:

SELECT TOP 100
    (columns snipped)
FROM (
    SELECT 
        CASE WHEN (e2.ID IS NULL) THEN
            CAST(0 AS BIT) ELSE CAST(1 AS BIT) END AS RecordExists,
        (columns snipped)
    FROM dbo.Files AS e1
    LEFT OUTER JOIN dbo.Records AS e2 ON e1.FID = e2.FID
)  AS p1
ORDER BY p1.RecordExists

基本上,我通过Files是否有相应的Record来排序结果,因为那些不需要首先处理。我可以使用WHERE子句运行两个查询,但如果可能的话,我宁愿在一个查询中进行。

有什么办法可以加快这个速度吗?

4

6 回答 6

3

最终的问题是,在子查询中使用 CASE 会引入 ORDER BY,而不是以sargable方式使用的东西。因此,必须首先对整个中间结果集进行排序以找到 TOP 100 - 这是全部 350+ 百万条记录!2

在这种特殊情况下,将 CASE 移动到外部 SELECT 并使用 DESC 排序(首先放置 NULL 值,这意味着当前 RecordExists 中的“0”)应该可以解决问题1虽然这不是一种通用方法..但是如果 Files.ID被索引,排序应该快得多。(如果查询仍然很慢,请查阅查询计划以找出 ORDER BY使用索引的原因。)

另一种选择可能是为 RecordExists 包含一个持久计算列(也被索引),它可以用作 ORDER BY 中的索引。

再一次,这个想法是 ORDER BY 可以处理一些sargable,它只需要在索引内按顺序读取(直到所需的记录数以匹配外部限制),而不是即时订购 350+ 百万条记录: )

然后 SQL Server 能够将此排序(和限制)下推到子查询中,而不是等待子查询的中间结果集出现。根据订购的内容查看查询计划差异。


1示例:

SELECT TOP 100
    -- If needed
    CASE WHEN (p1.ID IS NULL) THEN
        CAST(0 AS BIT) ELSE CAST(1 AS BIT) END AS RecordExists,
    (columns snipped)
FROM (
    SELECT 
        (columns snipped)
    FROM dbo.Files AS e1
    LEFT OUTER JOIN dbo.Records AS e2 ON e1.FID = e2.FID
)  AS p1
-- Hopefully ID is indexed, DESC makes NULLs (!RecordExists) go first
ORDER BY p1.ID DESC

2实际上,它似乎可以假设只是在前 100 个 0 之后停止而没有全排序 .. 至少在封闭函数范围下的一些极端查询计划优化下,但这取决于中间结果中何时遇到 0设置(在最初的几千或直到数亿或永远不会?)。无论如何,我非常怀疑SQL Server 是否会导致这种极端情况。也就是说,不要指望这种仍然不可预测的行为。

于 2012-09-20T22:44:11.710 回答
2

试试这个表格

SELECT TOP(100) *
FROM (
    SELECT TOP(100)
        0 AS RecordExists
        --,(columns snipped)
    FROM dbo.Files AS e1
    WHERE NOT EXISTS (SELECT * FROM dbo.Records e2 WHERE e1.FID = e2.FID)
    ORDER BY SecondaryOrderColumn
) X
UNION ALL
SELECT * FROM (
    SELECT TOP(100)
        1 AS RecordExists
        --,(columns snipped)
    FROM dbo.Files AS e1
    INNER JOIN dbo.Records AS e2 ON e1.FID = e2.FID
    ORDER BY SecondaryOrderColumn
) X
ORDER BY SecondaryOrderColumn

关键索引:
记录(FID)
文件(FID、SecondaryOrdercolumn)

于 2012-09-20T22:43:19.913 回答
1

那么它慢得多的原因是因为它实际上是一个非常不同的查询,没有 order by 子句。

使用 order by 子句:从整个 3.5 亿行中查找所有匹配的记录。然后对它们进行排序。

没有 order by 子句:查找前 100 条匹配记录。停止。

于 2012-09-20T22:41:22.003 回答
0

问:如果您说唯一的区别是“with/outout”和“order by”,那么您能否以某种方式将“top 100”移动到内部选择中?

例子:

SELECT
    (columns snipped)
FROM (
    SELECT TOP 100
        CASE WHEN (e2.ID IS NULL) THEN
            CAST(0 AS BIT) ELSE CAST(1 AS BIT) END AS RecordExists,
        (columns snipped)
    FROM dbo.Files AS e1
    LEFT OUTER JOIN dbo.Records AS e2 ON e1.FID = e2.FID
)  AS p1
ORDER BY p1.RecordExists
于 2012-09-20T22:43:38.373 回答
0

在 SQL Server 中,null值排序低于域中的任何值。鉴于这两个表:

create table dbo.foo
(
  id   int         not null identity(1,1) primary key clustered ,
  name varchar(32) not null unique nonclustered ,
)

insert dbo.foo ( name ) values ( 'alpha' )
insert dbo.foo ( name ) values ( 'bravo' )
insert dbo.foo ( name ) values ( 'charlie' )
insert dbo.foo ( name ) values ( 'delta' )
insert dbo.foo ( name ) values ( 'echo' )
insert dbo.foo ( name ) values ( 'foxtrot' )
go

create table dbo.bar
(
  id     int         not null identity(1,1) primary key clustered ,
  foo_id int             null foreign key references dbo.foo(id) ,
  name   varchar(32) not null unique nonclustered ,
)
go
insert dbo.bar( foo_id , name ) values( 1 , 'golf' )
insert dbo.bar( foo_id , name ) values( 5 , 'hotel' )
insert dbo.bar( foo_id , name ) values( 3 , 'india' )
insert dbo.bar( foo_id , name ) values( 5 , 'juliet' )
insert dbo.bar( foo_id , name ) values( 6 , 'kilo' )
go

查询

select *
from      dbo.foo foo
left join dbo.bar bar on bar.foo_id = foo.id
order by bar.foo_id, foo.id

产生以下结果集:

id name    id   foo_id name
-- ------- ---- ------ -------
2  bravo   NULL NULL   NULL
4  delta   NULL NULL   NULL
1  alpha   1    1      golf
3  charlie 3    3      india
5  echo    2    5      hotel
5  echo    4    5      juliet
6  foxtrot 5    6      kilo

(7 row(s) affected)

应该允许查询优化器使用合适的索引(如果存在);但是,它不保证会使用任何此类索引。

于 2012-09-20T23:12:39.617 回答
0

你能试试这个吗?

SELECT TOP 100
    (columns snipped)
FROM dbo.Files AS e1
LEFT OUTER JOIN dbo.Records AS e2 ON e1.FID = e2.FID
ORDER BY e2.ID ASC

这应该首先为您提供 e2.ID 为空的位置。此外,请确保 Records.ID 已编入索引。这应该给你你想要的顺序。

于 2012-09-20T23:38:59.087 回答