从其他两个表格中加入一列的最佳方法

时间:2018-03-13 12:46:37

标签: sql oracle performance

我在Oracle中有类似以下的架构

部分:

+--------+----------+
| 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列的Section和Item中。 Item_version在item_ID上编制索引,但没有FK到Item.item_ID(快照组中的空间用完)。

我有接收版本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和Item_Version应该相似(1000s),而Section应该很小。

编辑: 我刚刚发现,架构显然没有FK。将忽略架构配置文件中指定的FK。他们只是在那里提供文档。因此,加入FK列之间没有区别。话虽这么说,通过将连接更改为一系列SELECT IN,我能够避免连接整个Item表两次。我不喜欢最终的查询,我也不太了解其中的差异,但统计数据表明它的工作要少得多(更改A-Rows从最内部扫描返回的部分来自656,000到488(以前是656k开始返回1行,现在它的488开始返回1行)。

1 个答案:

答案 0 :(得分:0)

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

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 <)
    )
)