6

我在 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 表中有数百万行数据。上有一个索引CCSD.G_ID,所以不能缺少索引。

我只是不明白为什么SELECT *如果您执行其他任何操作,立即返回零行的查询需要这么长时间?任何人都可以对此有所了解吗?

更新

这是SELECT * FROM...查询的解释计划: 解释计划1

这是SELECT COUNT(*) FROM...查询的解释计划: 在此处输入图像描述

4

2 回答 2

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)

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

于 2013-10-28T18:22:56.990 回答
3

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

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

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

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

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

于 2013-10-28T18:18:07.843 回答