16

我正在从表值函数中选择一些行,但通过将 SELECT TOP 放入查询中发现了莫名其妙的巨大性能差异。

SELECT   col1, col2, col3 etc
FROM     dbo.some_table_function
WHERE    col1 = @parameter
--ORDER BY col1

需要 5 或 6 分钟以上才能完成。

然而

SELECT   TOP 6000 col1, col2, col3 etc
FROM     dbo.some_table_function
WHERE    col1 = @parameter
--ORDER BY col1

在大约 4 或 5 秒内完成。

如果返回的数据集很大,这不会让我感到惊讶,但所涉及的特定查询会返回 200,000 行中的约 5000 行

因此,在这两种情况下,都会处理整个表,因为 SQL Server 会继续搜索它永远不会到达的 6000 行。那为什么会有巨大的差异呢?这是否与 SQL Server 在预期结果集大小时分配空间的方式有关(TOP 6000 从而使其要求较低,更容易在内存中分配)?有没有其他人目睹过这样的事情?

谢谢

4

6 回答 6

8

表值函数可以具有非线性执行时间。

让我们考虑这个查询的等效函数:

SELECT  (
        SELECT  SUM(mi.value)
        FROM    mytable mi
        WHERE   mi.id <= mo.id
        )
FROM    mytable mo
ORDER BY
        mo.value

这个查询(计算 running SUM)在开始时很快,在结束时很慢,因为它的每一行都mo应该对所有前面的值求和,这需要倒回行源。

SUM随着行数的增加,计算每行所需的时间也会增加。

如果你做得mytable足够大(比如说,100,000行,如你的例子)并运行这个查询,你会发现它需要相当长的时间。

但是,如果您应用此查询,您会发现它的完成速度比完整表所需的时间TOP 5000快得多。1/20

最有可能的是,您的情况也发生了类似的事情。

说得更明确一些,我需要看一下函数定义。

更新:

SQL Server可以将谓词推送到函数中。

例如,我刚刚创建了这个TVF

CREATE FUNCTION fn_test()
RETURNS TABLE
AS
RETURN  (
        SELECT  *
        FROM    master
        );

这些查询:

SELECT  *
FROM    fn_test()
WHERE   name = @name

SELECT  TOP 1000 *
FROM    fn_test()
WHERE   name = @name

产生不同的执行计划(第一个使用聚集扫描,第二个使用带有 a 的索引查找TOP

于 2009-09-08T11:45:37.600 回答
5

我遇到了同样的问题,一个简单的查询连接五个返回 1000 行的表需要两分钟才能完成。当我在其中添加“TOP 10000”时,它在不到一秒的时间内完成。事实证明,其中一张表上的聚集索引严重碎片化。

重建索引后,查询现在在不到一秒的时间内完成。

于 2015-07-03T07:59:46.187 回答
4

您的 TOP 没有 ORDER BY,因此它与首先设置 ROWCOUNT 6000 相同。ORDER BY 将要求首先评估所有行,并且需要更长的时间。

如果dbo.some_table_function是一个值为 udf 的内联表,那么它只是一个扩展的宏,因此它返回前 6000 行,如前所述,没有特定的顺序。

如果 udf 是多值的,那么它就是一个黑匣子,并且总是会在过滤之前拉入完整的数据集。我不认为这正在发生。

没有直接关系,但是关于 TVF 的另一个 SO 问题

于 2009-09-08T12:14:57.937 回答
1

我认为 Quassnois 的建议似乎很合理。通过添加 TOP 6000,您隐含地向优化器提示将返回 200,000 行中的一小部分。然后优化器使用索引查找而不是聚集索引扫描或表扫描。

正如 Jim davis 所建议的,另一种可能的解释是缓存。这很容易通过再次运行查询来排除。尝试先运行 TOP 6000 的那个。

于 2009-09-08T12:48:50.350 回答
1

如果 col1 有索引,则不一定要处理整个表。

SQL 优化会选择是否使用索引。也许您的“TOP”迫使它使用索引。

如果您使用的是 MSSQL 查询分析器(我不知道这个名字),请按 Ctrl-K。这将显示查询的执行计划,而不是执行它。我相信,将鼠标悬停在图标上会显示 IO/CPU 使用情况。

我敢打赌,一个正在使用索引搜索,而另一个则没有。

如果你有一个通用客户端:SET SHOWPLAN_ALL ON; 去选择...; 去

有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/ms187735.aspx

于 2009-09-08T11:53:03.323 回答
1

您可能会遇到像在这里缓存一样简单的事情——也许(无论出于何种原因)“TOP”查询被缓存了?使用另一个不是的索引?

无论如何,消除好奇心的最好方法是检查两个查询的完整执行计划。您可以在 SQL 管理控制台中正确执行此操作,它会准确地告诉您正在完成哪些操作以及每个操作预计需要多长时间。

所有的 SQL 实现都各有特色——SQL Server 也不例外。这种“哇哇哇?!” 时刻很常见。;^)

于 2009-09-08T11:56:50.993 回答