我遇到了一些依赖于子查询的查询的奇怪问题。它们快速运行,直到我在子查询中使用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。
问题解决了。请参阅下面的答案。
答案 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);