[英]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.