查询在条件 IF 中变慢

Posted

技术标签:

【中文标题】查询在条件 IF 中变慢【英文标题】:Query slows down in Conditional IF 【发布时间】:2014-11-25 00:14:04 【问题描述】:

我注意到在 SQL Server (2008 R2) 中运行以下代码需要 32 秒

IF  ((SELECT COUNT(*) FROM view1) != 0)
    OR ((SELECT COUNT(*) FROM view2) != 0)
    OR ((SELECT COUNT(*) FROM view3) != 0)
    OR ((SELECT COUNT(*) FROM view4) != 0)
    OR ((SELECT COUNT(*) FROM view5) != 0)
    OR ((SELECT COUNT(*) FROM view6) != 0)
        PRINT 'HI'

然而,以下只用了 4 秒

SELECT 
    (SELECT COUNT(*) FROM view1)
    , (SELECT COUNT(*) FROM view2)
    , (SELECT COUNT(*) FROM view3)
    , (SELECT COUNT(*) FROM view4)
    , (SELECT COUNT(*) FROM view5)
    , (SELECT COUNT(*) FROM viwe6)

我已经设法通过执行 SELECT COUNT(1)... + SELECT... != 0 来优化条件,这需要 4 秒,但查看执行计划并没有产生任何特别值得注意的结果。也没有太多机会用谷歌搜索这个词。

有人可以分享一下 SQL Server 优化器可能在背后做什么吗?

编辑:IF EXISTS 尝试耗时 38 秒。

IF EXISTS ((SELECT 1 FROM view1)
    UNION (SELECT 1 FROM view2)
    UNION (SELECT 1 FROM view3)
    UNION (SELECT 1 FROM view4)
    UNION (SELECT 1 FROM view5)
    UNION (SELECT 1 FROM view6))
        PRINT 'HI'

EDIT2:当前的 5 秒方法。

IF  (SELECT COUNT(1) FROM view1)
  + (SELECT COUNT(1) FROM view2)
  + (SELECT COUNT(1) FROM view3)
  + (SELECT COUNT(1) FROM view4)
  + (SELECT COUNT(1) FROM view5)
  + (SELECT COUNT(1) FROM viwe6) != 0
        PRINT 'HI'

EDIT3:在比较了 20 页大小的查询计划之后 - 似乎加速主要是由于底层视图在它们的连接之前进行部分聚合,而不是先连接然后聚合。

【问题讨论】:

执行 EXISTS( SELECT 1 FROM view1 ) 可以获得更好的性能,因为它会在找到一行后停止查看,而不是计算所有行。 我确实尝试过 EXISTS。但这也花了大约 38 秒。我会将我尝试过的内容放入编辑中。 快速思考将计数结果放入变量然后使用 IF 条件 嵌套视图可能会导致查询优化器出现问题,尤其是当它们深入时。经验法则是遵循百分比并单独攻击大百分比。 【参考方案1】:

使用 TOP 1 语句:

例如:

DECLARE @CONTROL INT;
WITH CTE AS (
SELECT TOP 1 col FROM view1
UNION ALL
SELECT TOP 1 col FROM view2
UNION ALL
SELECT TOP 1 col FROM view3
UNION ALL
SELECT TOP 1 col FROM view4
UNION ALL
SELECT TOP 1 col FROM view5
UNION ALL
SELECT TOP 1 col FROM view6)

SELECT @CONTROL = COUNT(col) FROM CTE

IF @CONTROL != 0
PRINT 'HI'

【讨论】:

【参考方案2】:

运行下面的代码需要多长时间?如果您将查询转换为EXISTS,您将希望避免使用UNION,因为在您的示例中,您强制SQL 将每个视图中的每一行的SQL 转换为UNION,然后查看元素是否存在。下面的答案将是机会主义尽快停止。

IF EXISTS(SELECT 1 FROM view1)
    OR EXISTS(SELECT 1 FROM view2)
    OR EXISTS(SELECT 1 FROM view3)
    OR EXISTS(SELECT 1 FROM view4)
    OR EXISTS(SELECT 1 FROM view5)
    OR EXISTS(SELECT 1 FROM view6)
    PRINT 'HI'

这是 TOP 与单个存在的替代方法 - 也许基于提供的解释,为什么多个 EXISTS 执行如此缓慢,对您来说应该更好。

IF EXISTS (
    SELECT TOP (1) 1 FROM view1
    UNION ALL SELECT TOP (1) 1 FROM view2
    UNION ALL SELECT TOP (1) 1 FROM view3
    UNION ALL SELECT TOP (1) 1 FROM view4
    UNION ALL SELECT TOP (1) 1 FROM view5
    UNION ALL SELECT TOP (1) 1 FROM view6
    )
    PRINT 'Hi'

至于您在后台的问题,下面的网址有一个很好的写法: http://sqlserverplanet.com/tsql/comparing-exists-vs-left-join-where-not-null

基本上,它描述了使用EXISTS时未创建工作表,因此使用带有多个EXISTS的单个语句,性能可能会像您的情况一样变慢。

【讨论】:

运气不好,这花了 29 秒。我推测问题不是尽快“退出” - 而是由于 if 语句,底层 SQL 优化引擎执行计划不佳。我仍在质疑它为什么以及它可能在幕后做什么。 您可以运行备用查询吗?我添加了一些信息和博客文章的链接,说明为什么像这样的多个 EXISTS 在某些情况下会变慢。 有趣的是上述两个查询的执行计划是相同的,但据说单个 EXISTS 应该更优化 备用查询是 28 秒 - 所以它单存在确实更优化

以上是关于查询在条件 IF 中变慢的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 调试器在内联代码中变慢

为啥IE突然变慢?

添加 where 条件使 MDX 查询变慢(olap4j)

kettle中如果用传参查询会变慢是啥原因

Redis都有哪些慢操作?

数据量太大,分页查询变慢,有啥优化查询的方法吗