SQL Select 执行时间过长

Smu*_*mur 10 sql-server-2005 sql-server query statistics

这是从临时表中进行的简单选择,在其主键上加入现有表,两个子选择使用 top 1 引用连接表。

在代码中:

SELECT
    TempTable.Col1,
    TempTable.Col2,
    TempTable.Col3,
    JoinedTable.Col1,
    JoinedTable.Col2,
    (
        SELECT TOP 1
            ThirdTable.Col1 -- Which is ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn1,
    (
        SELECT TOP 1
            ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn2,
FROM
    #TempTable as TempTable
LEFT JOIN
    JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn1 AND 
    TempTable.PKColumn2 = JoinedTable.PKColumn2)
WHERE
    JoinedTable.WhereColumn IN  (1, 3)
Run Code Online (Sandbox Code Playgroud)

这是我的查询的精确副本。

如果我删除两个子选择,它运行得很好而且很快。通过两个子选择,我每秒得到大约 100 条记录,这对于这个查询来说非常慢,因为它应该返回近一百万条记录。

我已经检查过是否每张表都有一个主键,他们都有。它们都有重要列的索引和统计信息,就像那些 WHERE 子句中的那些,以及 JOIN 子句中的那些。唯一没有定义主键和索引的表是临时表,但这也不是问题,因为它与慢速子选择无关,而且正如我所提到的,没有子选择它运行得很好。

如果没有这些,TOP 1它会返回多个结果,并引发错误。

帮助,有人吗?

编辑

所以执行计划告诉我我缺少一个索引。我已经创建了它,并重新创建了一些其他索引。过了一会儿,执行计划开始使用它们,查询现在运行得很快。唯一的问题是我没有成功在另一台服务器上再次执行相同的查询。所以我的解决方案是提示 SQL Server 将使用哪个索引。

小智 7

我认为在一百万条记录查询中,您必须避免诸如OUTER JOINS. 我建议你使用UNION ALL而不是LEFT JOIN. 只要我认为CROSS APPLYselect子句中的子查询效率更高,我就会修改Conard Frix编写的查询,我认为这是正确的。

现在:当我开始修改您的查询时,我注意到您有一个 WHERE 子句说: JoinedTable.WhereColumn IN (1, 3)。在这种情况下,如果该字段为空,则条件将变为假。那么为什么在过滤空值行时使用 LEFT JOIN 呢?只需替换LEFT JOINWith INNER JOIN,我保证它会变得更快。

关于指数:

请注意,当您在表上有索引时,请说

table1(a int, b nvarchar)
Run Code Online (Sandbox Code Playgroud)

你的索引是:

nonclustered index ix1 on table1(a)
Run Code Online (Sandbox Code Playgroud)

你想做这样的事情:

select a,b from table1
where a < 10
Run Code Online (Sandbox Code Playgroud)

在您的索引中您没有包含该列b,那么会发生什么?

如果 sql-server 使用您的索引,它将必须在索引中搜索,称为“索引查找”,然后引用主表以获取列b,称为“查找”。此过程可能比扫描表本身花费的时间长得多:“表扫描”

但根据 sql-server 的统计数据,在这种情况下,它可能根本不使用您的索引。

所以首先检查Execution Plan索引是否被使用。

如果是或否两者,请更改您的索引以包括您选择的所有列。像这样说:

nonclustered index ix1 on table1(a) include(b)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,将不需要查找,并且您的查询将执行得更快。


小智 6

它是导致缓慢返回的列选择中的子选择。您应该尝试在左连接中使用您的子选择,或者使用我在下面定义的派生表。

对第三个表的两个实例使用左连接

SELECT
  TempTable.Col1,
  TempTable.Col2,
  TempTable.Col3,
  JoinedTable.Col1,
  JoinedTable.Col2,
  ThirdTable.Col1 AS ThirdTableColumn1,
  ThirdTable2.Col1 AS ThirdTableColumn2
FROM #TempTable as TempTable
LEFT JOIN JoinedTable ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND 
    TempTable.PKColumn 2 = JoinedTable.PKColumn2)
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
WHERE
    JoinedTable.WhereColumn IN  (1, 3)
Run Code Online (Sandbox Code Playgroud)

使用派生表

 SELECT 
      TempTable.Col1,
      TempTable.Col2,
      TempTable.Col3,
      DerivedTable.Col1,
      DerivedTable.Col2,
      DerivedTable.ThirdTableColumn1,
      DerivedTable.ThirdTableColumn2
 FROM #TempTable as TempTable
    LEFT JOIN (SELECT
                 JoinedTable.PKColumn2,
                 JoinedTable.Col1,
                 JoinedTable.Col2,
                 JoinedTable.WhereColumn,
                 ThirdTable.Col1 AS ThirdTableColumn1,
                 ThirdTable2.Col1 AS ThirdTableColumn2
               FROM JoinedTable
               LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
               LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn) 
        DerivedTable ON (TempTable.PKColumn1 = DerivedTable .PKColumn2 AND 
        TempTable.PKColumn2 = DerivedTable.PKColumn2)
    WHERE
        DerivedTable.WhereColumn IN  (1, 3)
Run Code Online (Sandbox Code Playgroud)