SQL Select花费太多时间执行


9

这是从临时表中进行的简单选择,左键将现有表保留在其主键上,其中两个子选择使用前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)

这是我查询的精确副本。

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

我检查了每个表是否都有主键,它们都一样。它们的重要列都具有索引和统计信息,例如那些WHERE子句中的那些和JOIN子句中的那些。唯一没有定义主键或索引的表是临时表,但这不是问题,因为它不是与慢速子选择相关的表,而且正如我提到的那样,没有子选择,它运行得很好。

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

帮忙,有人吗?

编辑

因此执行计划告诉我我缺少索引。我已经创建了它,并重新创建了其他一些索引。一段时间后,执行计划正在使用它们,现在查询运行很快。唯一的问题是,对于同一查询,我没有在另一台服务器上再次成功执行此操作。因此,我的解决方案将是提示SQL Server将使用哪个索引。


哇,真令人印象深刻。但是您可以将其拆分为多个单独的语句吗?另外,存储过程又如何呢?

2
@Adel该选择实际上是存储过程内的子选择。整个事情实际上很大,但是我100%确信那是花费时间执行的确切部分。

执行计划中的更改(包括自动选择的索引)最有可能与数据更改有关。我将确保您的索引被完全覆盖,否则引擎将采用意外的路径,例如表扫描。我建议检查新服务器上的执行计划(不带提示),以查看与原始系统有偏差的地方。
罗伯特·米勒

我知道了。我只更改了服务器,数据库是相同的,具有相同的索引。尽管如此,它似乎并没有自动选择使用我的索引。它完全符合您的意思:表格扫描。
Smur,

听起来查询优化器不喜欢查询的任何表索引。执行计划是否显示缺少索引?
罗伯特·米勒

Answers:


7

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

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

关于INDEX:

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

table1(a int, b nvarchar)

而您的索引是:

nonclustered index ix1 on table1(a)

并且您想要执行以下操作:

select a,b from table1
where a < 10

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

如果sql-server使用您的索引,则必须在索引中搜索“ Index Seek”,然后引用主表以获取列b,称为“ Look Up”。该过程可能比扫描表本身花费更多的时间:“ Table Scan”

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

因此,首先检查Execution Plan,看看是否使用了索引。

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

nonclustered index ix1 on table1(a) include(b)

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


1
我不能将左连接更改为内部连接,这会弄乱结果,这是一个业务规则:第二个表不一定必须具有相关记录。另外,WHERE子句中的列不接受空值。
Smur,

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)

使用派生表

 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)

2

尝试交叉申请

SELECT
    TempTable.Col1,
    TempTable.Col2,
    TempTable.Col3,
    JoinedTable.Col1,
    JoinedTable.Col2,
    ThirdTableColumn1.col1,
    ThirdTableColumn2.col1

FROM
    #TempTable as TempTable
LEFT JOIN
    JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND 
    TempTable.PKColumn 2 = JoinedTablePKColumn2)

CROSS APPLY
(
        SELECT TOP 1
            ThirdTable.Col1 -- Which is ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn1
CROSS APPLY    (
        SELECT TOP 1
            ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn2,
WHERE
    JoinedTable.WhereColumn IN  (1, 3)

您还可以使用CTE和row_number或使用MIN的内联查询


2

将JOIN位移出该子句的主要部分,并将其作为子选择。将其移至WHERE和JOIN部分可确保您不必一遍又一遍地选择TOP 1,我相信这是运行缓慢的原因。如果要检查,请检查执行计划。


2

ThirdTable参考,(在你的例子子选择),需要同样关注指数作为查询的任何其他部分。

不管您是否使用子选择:

(
    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,

左联接(由John Hartsock提议):

LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn

交叉申请(由Conrad Frix提议):

CROSS APPLY
(
        SELECT TOP 1
            ThirdTable.Col1 -- Which is ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn1
CROSS APPLY    (
        SELECT TOP 1
            ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn2

您需要确保covering indexesThirdTable.SomeColumn和定义ThirdTable.SomeOtherColumn了索引,并且索引是唯一的。这意味着您将需要进一步限定ThirdTable引用的数量,以消除对多行的选择并提高性能。的选择sub selectsLEFT JOINCROSS APPLY将没有真正的问题,直到你提高的选择性ThirdTable.SomeColumnThirdTable.SomeOtherColumn由包括多个列,以确保独特的选择性。在那之前,我希望您的表现会继续受到影响。

covering indexMaziar Taheri很好地介绍了该主题。在不重复他的工作的同时,我确实强调需要牢记涵盖索引的使用。

简而言之:通过添加相关的表内列以确保唯一的行匹配,提高ThirdTable.SomeColumnand ThirdTable.SomeOtherColumn查询(或联接)的选择性。如果这不可能,那么您将继续遇到性能问题,因为引擎正忙于拉入随后被丢弃的行。这会影响您的I / O,CPU和最终的执行计划。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.