Oracle 11g - 为什么SELECT COUNT(*)比SELECT *慢得多?

时间:2013-10-28 17:27:08

标签: sql oracle oracle11g

我在Oracle 11g中有这样的查询:

SELECT *
FROM CATAT, CG, CCSD
WHERE CATAT.ID = 1007642
AND CG.C_ID = CATAT.ID
AND CATAT.IS_PARENT = 1
AND CCSD.G_ID = CG.ID

在这种情况下,查询返回零行,并且几乎立即执行。但是,如果我将其更改为:

SELECT COUNT(*) AS ROW_COUNT
FROM CATAT, CG, CCSD
WHERE CATAT.ID = 1007642
AND CG.C_ID = CATAT.ID
AND CATAT.IS_PARENT = 1
AND CCSD.G_ID = CG.ID

它永远不会回来 - 我已经让查询运行了超过5分钟但仍然没有完成。事实上,除SELECT *之外的任何事情都需要很长时间才能运行。例如。 SELECT CG.ID FROM...SELECT CATAT.* FROM...

此查询唯一不寻常的是CCSD表中包含数百万行数据。 <{1}}上有一个索引,因此不能缺少索引。

我只是不明白为什么如果你做了除此之外的任何事情,那么使用CCSD.G_ID立即返回零行的查询应该花费这么长时间?任何人都可以对此有所了解吗?

更新

以下是SELECT *查询的解释计划: explain plan 1

以下是SELECT * FROM...查询的解释计划: enter image description here

2 个答案:

答案 0 :(得分:7)

如果您改为运行此查询会发生什么?

SELECT COUNT(*) AS ROW_COUNT
FROM CATAT
WHERE CATAT.ID = 1007642
AND CATAT.IS_PARENT = 1
AND EXISTS(SELECT 1 FROM CG WHERE CG.C_ID = CATAT.ID AND EXISTS(SELECT 1 FROM CCSD WHERE CCSD.G_ID = CG.ID))

我认为问题出在你在查询中的双重连接中,

希望它有所帮助!

修改

在原始查询中详细说明:

SELECT COUNT(*) AS ROW_COUNT
**FROM CATAT, CG, CCSD**
WHERE CATAT.ID = 1007642
AND CG.C_ID = CATAT.ID
AND CATAT.IS_PARENT = 1
AND CCSD.G_ID = CG.ID

第二行是问题,当您在Oracle的from子句中列出其他表时,这意味着您正在编写隐式连接IF,并且仅当您列出并匹配每个表上的所有主键与另一列上的另一列时表。根据您在where子句中添加的主键组件,它将导致常规内部联接(如果您匹配所有主键列),或者它可能导致类似于笛卡尔积的类似于我认为计划的情况你发布了图片,我可以在查询计划中看到合并加入选项笛卡尔。

所有这些意味着数据库正在生成一个非常大的表,并且该表中的行数是CCSD中的所有行* CG中的所有行* CATAT中的所有行(CCSD有几百万作为你如此说明这导致你感觉到的缓慢),然后试图遍历这个临时表,检查你所拥有的过滤器。

这个问题正在发生,因为原始查询没有针对任务进行优化,我发布的是。

我所做的是读你查询以了解你想要做什么,你试图列出具有特定ID和IS_PARENT = 1的CATAT表的子集,但你只想列出那些ID(CATAT.ID)在表CG和表CCSD中打开(或存在)的那些。在编写查询时,我尝试使用条件中相同的级联,但我最初发布的查询也可以这样写:

SELECT COUNT(*) AS ROW_COUNT
FROM CATAT
WHERE CATAT.ID = 1007642
AND CATAT.IS_PARENT = 1
AND EXISTS(SELECT 1 FROM CG WHERE CG.C_ID = CATAT.ID )
AND EXISTS(SELECT 1 FROM CCSD WHERE CCSD.G_ID = CATAT.ID)

现在,此查询与您编写的原始查询完全相同,但没有连接。为了解决这个问题,数据库遍历IDAT和IS_PARENT匹配的表CATAT(有一个索引使得它真的很快),一旦一行符合前两个条件,数据库试图通过表CG上的C_ID找到现有记录(再次真的如果你有一个索引,则快速),之后它会尝试通过ID对表CCSD执行相同的操作。在我发布的第一个查询中,最后2个搜索是级联的,但是想法是相同的:您的查询运行缓慢,因为创建了一个笛卡尔积(可能是优化的,但仍导致大量行)而我的那个write只是按ID遍历表(没有合并),这些列可能已经在这些列中有索引,这就是它运行速度快的原因。

答案 1 :(得分:3)

如果您在某些SQL开发环境(如Toad或SQL Developer)中评估查询的性能,则不是真正的比较。大多数IDE都会获取第一个 n 行(通常为50行)。 使用

包装查询
SELECT * FROM (your query) WHERE ROWNUM <= 50

通常有一个停止键提示。这意味着DB只获取前50行并停止。但是,您的SELECT COUNT(*) FROM ...强制数据库实际计算查询返回的每一行,并且需要花费的时间。

编辑:当我说你的SQL Developer查询包含在rownum查询中时,我想到了另一个Oracle产品(Apex)。那是不对的。显然,SQL Developer会根据您的偏好为您的会话设置数组大小。然而,获取50行并停止将总是比强制所有行的计数更快。

编辑2:很公平,我以为我理解这个问题和SQL Developer获取大小,但不是。我会在这里留下我的答案作为假设的警示。