联接其他两个表中列上的一列的最佳方法



>我在甲骨文中有一个如下所示的模式

部分:

+--------+----------+
| sec_ID | group_ID |
+--------+----------+
|    1   |     1    |
|    2   |     1    |
|    3   |     2    |
|    4   |     2    |
+--------+----------+

Section_to_Item:

+--------+---------+
| sec_ID | item_ID |
+--------+---------+
|    1   |     1   |
|    1   |     2   |
|    2   |     3   |
|    2   |     4   |
+--------+---------+

项目:

+---------+------+
| item_ID | data |
+---------+------+
|    1    |  a   |
|    2    |  b   |
|    3    |  c   |
|    4    |  d   |
+---------+------+

Item_Version:

+---------+----------+--------+
| item_ID | start_ID | end_ID |
+---------+----------+--------+
|    1    |    1     |        |
|    2    |    1     |    3   |
|    3    |    2     |        |
|    4    |    1     |    2   |
+---------+----------+--------+

Section_to_Item 将 FK 放入 *_ID 列上的部分和项目。 Item_version在item_ID上编制索引,但没有要Item.item_ID的 FK(快照组中的空间不足)。

我有接收版本 ID 列表的代码,我想获取给定组中至少对传入的一个版本有效的部分中的所有项目。如果某个项目没有end_ID,则它对以 start_ID 开头的任何内容都有效。如果它有end_id,则它对end_ID之前(不包括)的任何内容都有效。

我目前拥有的是:

SELECT Items.data
FROM Section, Section_to_Items, Item, Item_Version
WHERE Section.group_ID = 1
AND Section_to_Item.sec_ID = Section.sec_ID
AND Item.item_ID = Section_to_Item.item_ID
AND Item.item_ID = Item_Version.item_ID
AND exists (
SELECT *
FROM (
SELECT 2 AS version FROM DUAL
UNION ALL SELECT 3 AS version FROM DUAL
) passed_versions
WHERE Item_Version.start_ID <= passed_versions.version
AND (Item_Version.end_ID IS NULL or Item_Version.end_ID > passed_version.version)
)

请注意,UNION ALL 语句是从传入的版本列表动态生成的。

此查询当前执行笛卡尔连接,并且速度非常慢。 出于某种原因,如果我将查询更改为加入

AND Item_Version.item_ID = Section_to_Item.item_ID

它不是 FK,查询不执行笛卡尔连接并且速度更快。

A) 谁能解释为什么会这样?
B) 这是连接这一系列表的正确方法吗(我对将Item.item_ID连接到两个不同的表感到奇怪)
C) 这是在start_ID和end_ID之间获取版本的正确方法吗?

编辑

具有内部联接语法的相同查询:

SELECT Items.data
FROM Item
INNER JOIN Section_to_Items ON Section_to_Items.item_ID = Item.item_ID
INNER JOIN Section ON Section.sec_ID = Section_to_Items.sec_ID
INNER JOIN Item_Version ON Item_Version.item_ID = Item_.item_ID
WHERE Section.group_ID = 1
AND exists (
SELECT *
FROM (
SELECT 2 AS version FROM DUAL
UNION ALL SELECT 3 AS version FROM DUAL
) passed_versions
WHERE Item_Version.start_ID <= passed_versions.version
AND (Item_Version.end_ID IS NULL or Item_Version.end_ID > passed_version.version)
)

请注意,在这种情况下,性能差异来自先在Item_Version上联接,然后在 Item_Version.item_ID 上联接Section_to_Item。

就表格大小而言,Section_to_Item、项目和Item_Version应该相似(1000 秒),而部分应该很小。

编辑

我刚刚发现,显然,该架构没有 FK。将忽略架构配置文件中指定的 FK。他们只是为了记录。因此,加入 FK 列与否没有区别。话虽如此,通过将联接更改为 SELECT IN 的级联,我能够避免两次联接整个 Item 表。我不喜欢生成的查询,我真的不明白其中的区别,但统计数据表明它的工作要少得多(将从部分最内部扫描返回的 A 行从 656,000 更改为 488(以前是 656k 开始返回 1 行,现在是 488 开始返回 1 行))。

编辑

事实证明这是过时的统计数据 - 这两个查询始终是等效的,但由于统计数据不完整,数据库碰巧仅在第二个实例中注意到正确的计划。更新统计信息后,两个查询生成了相同的计划。

我不确定这是否是最好的主意,但这似乎避免了笛卡尔连接:

select data
from Item
where item_ID in (
select item_ID
from Item_Version
where item_ID in (
select item_ID
from Section_to_Item
where sec_ID in (
select sec_ID
from Section
where group_ID = 1
)
)
and exists (
select 1
from (
select 2 as version
from dual
union all
select 3 as version
from dual
) versions
where versions.version >= start_ID
and (end_ID is null or versions.version <)
)
)

相关内容

最新更新