簡體   English   中英

在其他兩個表的列上加入一列的最佳方法

[英]Best Way to Join One Column on Columns From Two Other Tables

我在 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_ID | start_ID | end_ID |
+---------+----------+--------+
|    1    |    1     |        |
|    2    |    1     |    3   |
|    3    |    2     |        |
|    4    |    1     |    2   |
+---------+----------+--------+

Section_to_Item 在 *_ID 列上具有 FK 到 Section 和 Item。 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,然后加入 Section_to_Item 和 Item_Version.item_ID。

在表大小方面,Section_to_Item、Item 和 Item_Version 應該相似(1000s),而 Section 應該很小。

編輯

我剛剛發現,顯然,該架構沒有 FK。 架構配置文件中指定的 FK 將被忽略。 它們只是用於文檔。 因此,是否加入 FK 列之間沒有區別。 也就是說,通過將聯接更改為級聯的 SELECT IN,我能夠避免兩次聯接整個 Item 表。 我不喜歡生成的查詢,我也不太理解其中的區別,但統計數據表明它的工作要少得多(將從 Section 的最內層掃描返回的 A-Rows 從 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 <)
    )
)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM