使用UNION子查询进行查询需要很长时间

时间:2011-08-08 15:41:06

标签: sql sql-server tsql azure-sql-database

我遇到了一些依赖于子查询的查询的奇怪问题。它们快速运行,直到我在子查询中使用UNION语句。然后他们无休止地奔跑,我在10分钟后给了他们。我现在描述的场景并不是我开始时的原始场景,但我认为它可以解决许多可能出现的问题但却产生同样的问题。所以即使这是一个毫无意义的问题,请耐心等待我!

我有一张桌子:

tblUser - 100,000 rows
tblFavourites - 200,000 rows

如果我执行:

SELECT COUNT(*) 
FROM tblFavourites 
WHERE userID NOT IN (SELECT uid FROM tblUser);  

...然后它在一秒钟内运行。但是,如果我修改它以使子查询具有UNION,它将运行至少10分钟(在我放弃之前!)

SELECT COUNT(*) 
FROM tblFavourites 
WHERE userID NOT IN (SELECT uid FROM tblUser UNION SELECT uid FROM tblUser);  

一个毫无意义的改变,但它应该产生相同的结果,我不明白为什么它应该再采取更长的时间?

将子查询放入视图并调用它会产生相同的效果。

任何想法为什么会这样?我正在使用SQL Azure。


问题解决了。请参阅下面的答案。


6 个答案:

答案 0 :(得分:3)

UNION实际上是对合并数据集中的所有字段执行DISTINCT。它会在最终结果中筛选掉dupes。

Uid被编入索引吗?如果不是,它可能需要很长时间作为查询引擎:

  • 生成第一个结果集
  • 生成第二个结果集
  • 过滤掉哈希表中的所有欺骗(记录的一半)

如果重复项不是问题(使用IN表示它们不会),则使用UNION ALL删除昂贵的排序/过滤步骤。

答案 1 :(得分:2)

UNION生成唯一值,因此DBMS引擎会进行排序。 在这种情况下,您可以安全地使用UNION ALL。

答案 2 :(得分:2)

UNION通常通过临时内存表实现。你基本上是将你的tblUser复制两次到内存中,没有索引。然后tblFavourites中的每一行都会产生超过200,000行的完整表扫描 - 这是200Kx200K = 400亿次双行扫描(因为查询引擎必须从两个表行中获取uid)

如果你的tblUser在uid上有一个索引(这肯定是正确的,因为SQL Azure中的所有表都必须有一个聚簇索引),那么tblFavourites中的每一行都会产生一个非常快速的索引查找,导致只有200Kxlog(100K)= 200Kx17 = 200K行扫描,每次扫描具有17个b树索引比较(这比从数据页上的行读取uid要快得多),因此它应该等于大约200Kx(3-4)或100万个双行扫描。我相信较新版本的SQL服务器也可能构建一个仅包含uid的临时哈希表,所以基本上它可以达到200K行扫描(假设哈希表查找很简单)。

您还应该生成要检查的查询计划。

基本上,如果tblUser有一个索引(应该在SQL Azure上),非UNION查询运行速度大约快500,000倍。

答案 3 :(得分:1)

事实证明问题是由于其中一个索引... tblFavourites在tblUser中包含了两个主键(uid)的外键:

userId
otherUserId

两列具有相同的定义和相同的索引,但我发现在原始查询中交换userId for otherUserId解决了这个问题。

我跑了:

ALTER INDEX ALL ON tblFavourites REBUILD

......问题消失了。查询现在几乎立即执行。

我不太了解Sql Server / Azure幕后发生的事情......但我只能想象它是一个损坏的索引还是什么?我经常更新统计数据,但这没有效果。

谢谢!

----更新

上述情况并不完全正确。它做了修复问题大约20分钟,然后它返回。我已经与微软的支持人员联系了几天,似乎问题与tempDB有关。他们正在努力寻找解决方案。

答案 4 :(得分:0)

我刚遇到这个问题。我有大约100万行要经过然后我意识到我的一些ID在另一个表中,所以我联合起来在一个“不存在”中获得相同的信息。我从大约7秒的查询开始到大约一分钟左右处理5000行。这似乎有所帮助。我绝对讨厌这个解决方案,但是我已经尝试过很多东西,这些东西都会以同样极其缓慢的执行计划结束。这个让我得到了我需要的大约18秒。

    DECLARE @PIDS TABLE ([PID] [INT] PRIMARY KEY)
    INSERT INTO @PIDS SELECT DISTINCT [ID] FROM [STAGE_TABLE] WITH(NOLOCK)
    INSERT INTO @PIDS SELECT DISTINCT [OTHERID] FROM [PRODUCTION_TABLE] WITH(NOLOCK)
        WHERE NOT EXISTS(SELECT [PID] FROM @PIDS WHERE [PID] = [OTHERID]

    SELECT (columns needed)
    FROM [ORDER_HEADER] [OH] WITH(NOLOCK)
    INNER JOIN @PIDS ON [OH].[SOME_ID] = [PID]

(是的,我尝试了“在哪里存在...”进行最终选择......内部连接速度更快) 请让我再说一遍,我个人认为这真的很难看,但我实际上在我的过程中使用了两次这样的连接,所以从长远来看,这将节省我的时间。希望这会有所帮助。

答案 5 :(得分:0)

中重新解释问题是不是更有意义

“UserIds不在此表格和/或该表格中所有ID的组合列表中”

“不在此表上且不在该表上的UserIds

SELECT COUNT(*) 
FROM tblFavourites 
WHERE userID NOT IN (SELECT uid FROM tblUser) 
AND   userID NOT IN (SELECT uid FROM tblUser);