2011-08-08 349 views
2

我一直对依赖子查询的某些查询存在奇怪的问题。他们运行得很快,直到我在子查询中使用UNION语句。然后他们无休止地跑,我在10分钟后给了。我现在描述的场景不是我开始使用的那个场景,但我认为它解决了很多可能的问题,但却产生了同样的问题。所以即使这是一个毫无意义的查询,请忍受我!使用UNION子查询进行查询需要很长时间

我有一个表:

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

因为查询会消除重复的结果,所以'UNION'将花费相当长的一段时间。它本质上是对结果集做一个DISTINCT。执行“UNION ALL”会得到更快的结果,但是如果存在重复项,它们将不会从结果集中消除。 –

+0

感谢您的快速响应。我将它改成了UNION ALL,并且在5分钟后仍然运行。但是,我将数据库下载到本地SQL Server 2008,问题消失了。 UNION的查询几乎和没有查询一样快。然后,我在单独的SQL Azure数据库上运行查询,并遇到同样的问题。所以它看起来像一个SQL Azure问题。我会联系Azure支持。谢谢! –

+0

每当联盟给我的问题,通常是因为它阻止自己。尝试做两个插入到表变量,然后在你的地方使用。 – cadrell0

回答

1

原来,问题是指数的,由于一个... tblFavourites包含两个外键在tblUser主键(UID):

userId 
otherUserId 

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

我跑:

ALTER INDEX ALL ON tblFavourites REBUILD 

...和问题走了。查询现在几乎立即执行。

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

谢谢!

---- UPDATE

以上不完全正确。它确实解决了大约20分钟的问题,然后它返回。我已经和微软的支持人员联系了好几天,看起来问题是关于tempDB。他们正在研究解决方案。

+0

如何从tblFavourites.userID到tblUser.uid并拥有'userID NOT IN(SELECT uid FROM tblUser)'返回true,除非userID为空?在这种情况下,测试'WHERE userID IS NULL'是更快的查询方式。 –

2

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

3

UNION对组合数据集中的所有字段确实在执行DISTINCT。它在最终结果中过滤掉了愚蠢的东西。

Uid索引?如果没有,可能需要很长的时间,因为查询引擎:

  • 生成的第一个结果集
  • 生成的第二个结果集
  • 筛选出所有的受骗者(其中一半的记录)在哈希表

如果重复是不是一个问题(以及使用IN意味着它们将不会被),然后使用UNION ALL其去除昂贵排序/过滤步骤。

+0

感谢您的快速响应。我将它改成了UNION ALL,并且在5分钟后仍然运行。但是,我将数据库下载到本地SQL Server 2008,问题消失了。 UNION的查询几乎和没有查询一样快。然后,我在单独的SQL Azure数据库上运行查询,并遇到同样的问题。所以它看起来像一个SQL Azure问题。我会联系Azure支持。谢谢! –

2

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

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

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

本质上,如果tblUser具有索引(应该在SQL Azure上),则非UNION查询的运行速度将快500,000倍。

0

我刚碰到这个问题。我有大约100万行要经过,然后我意识到我的一些身份证在另一张桌子上,所以我联合起来在一个“不存在”中获得相同的信息。一分钟左右后,我从查询花费约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] 

(是的,我试过“其中... EXISTS”的最终选择...内部联接是更快) 请让我再说一遍,我本人来说觉得这实在是太丑了,但其实我在我的过程中两次使用这个连接,所以从长远来看这将节省我的时间。希望这可以帮助。

0

难道不是更有意义从

“用户ID是不是在该表和/或表冲击片雷管的所有ID的组合列表上的”

改写的问题

“用户ID不在此表上,而不是在该表无论是

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