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

Sql Oracle 11g—为什么SELECT COUNT(*)比SELECT*慢得多?,sql,oracle,oracle11g,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

我在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*之外的任何操作都需要非常长的时间才能运行。例如,
从…
中选择CG.ID,或
从…

这个查询唯一不寻常的地方是CCSD表中有数百万行数据。在
CCSD.G_ID
上有一个索引,所以不能缺少索引

我只是不明白,如果您不这样做,为什么使用
SELECT*
立即返回零行的查询会花费这么长时间?有人能解释一下吗

更新 以下是
SELECT*FROM…
查询的解释计划:

以下是从…查询中选择计数(*)的解释计划:

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

SELECT * FROM (your query) WHERE ROWNUM <= 50

SELECT*FROM(您的查询)WHERE ROWNUM如果改为运行此查询会发生什么

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子句中列出其他表时,这意味着您正在编写隐式联接,当且仅当您将每个表上的所有主键与不同表上的另一列列出并匹配时。根据您在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)

现在,这个查询与您编写的原始查询完全相同,但没有连接。要解决此查询,数据库将遍历表CATAT,并按ID进行匹配,并且是父项(具有索引使其速度非常快),一旦行匹配前两个条件,数据库将尝试在表CG上按C_ID查找现有记录(如果有索引,则速度也非常快)之后,它尝试对CCSD by ID表执行相同的操作。最后2次搜索在我发布的第一个查询中是级联的,但想法是一样的:您的查询运行缓慢,因为正在创建笛卡尔乘积(可能已优化,但仍会产生大量行),而我编写的查询只是通过ID遍历表(没有合并),这些列中可能已经有索引,这就是它运行速度快的原因。

您可以为这两个查询输入解释计划吗?根据要求,我已经发布了解释计划!如果您尝试
从()中选择count(*)
,会发生什么情况?尝试count(1)而不是count(*)。Russel..count(*)和count(1)都是相同的。请参阅下面Tom Kyte的帖子,这通常是正确的,但是问题中的查询没有返回任何行(计数(*)给出0)-在这种情况下,Oracle必须从表中读取所有数据,最后才发现结果是空的。这是正确的,但SQL Developer等工具不使用ROWNUM条件包装查询,它们只是在需要时才会获取所有行。您好,我使用的是SQL Developer,但这种情况也发生在我用PHP编写的程序中,使用oci库。arraysize与客户端中实际显示的行数无关。它只是一个配置选项,用于在检索行时最大限度地减少网络往返。感谢您的回答!不知怎的,这很有效-您能解释一下原因吗?我编辑了答案,进一步解释了为什么它工作得更快,希望如此非常感谢您的解释-我认为我的程序中可能还有许多其他地方可以从这种类型的优化中受益!这是如何回答的?您没有解释为什么Oracle执行SELECT与COUNT(*)不同。您发布了原始查询,并说它有问题,但该查询没有问题,并且解释计划与count不同。问题不是Oracle为什么执行这两个查询不同,而是执行所需的时间,adobe对此进行了解释。