2009-01-23 71 views
6

我有一個像這樣的SQL表:表示單個SQL表中的記錄之間的多對多關係的最佳方式是什麼?

更新:我改變了示例表,因爲原始數據(州,城市,學校)的現有等級性質掩蓋了事實之間需要簡單的關係這幾項。

entities 
id  name    
1  Apple  
2  Orange    
3  Banana    
4  Carrot     
5  Mushroom   

我想定義這些實體之間的雙向關係,因此查看一個實體的用戶可以看到所有相關實體的列表。

關係由最終用戶定義。

在數據庫中表示這些關係並隨後查詢和更新它們的最佳方式是什麼?我看到它

一種方式...

我的直覺說,一個關係表如下所示:

entity_entity 
entity_id_a  entity_id_b 
1     2 
5     1 
4     1 
5     4 
1     3 

既然如此,鑑於4提供ENTITY_ID,怎麼會一個獲取所有相關記錄,這將是1和5?

同樣ENTITY_ID = 1的查詢應該返回2,3,4,5

感謝您的時間,讓我知道,如果我可以在任何澄清的問題。

回答

11

定義一個約束:entity_id_a < entity_id_b

創建索引:

CREATE UNIQUE INDEX ix_a_b ON entity_entity(entity_id_a, entity_id_b); 
CREATE INDEX ix_b ON entity_entity(entity_id_b); 

第二個索引並不需要包括entity_id_a因爲你只會用它來一個b內選擇所有a的。在ix_b上的RANGE SCAN將比在ix_a_b上的SKIP SCAN快。

填充表與實體如下:

INSERT 
INTO entity_entity (entity_id_a, entity_id_b) 
VALUES (LEAST(@id1, @id2), GREATEST(@id1, @id2)) 

然後選擇:

SELECT entity_id_b 
FROM entity_entity 
WHERE entity_id_a = @id 
UNION ALL 
SELECT entity_id_a 
FROM entity_entity 
WHERE entity_id_b = @id 

UNION ALL在這裏,您可以使用上述指標,避免了額外的獨特性排序。

以上所有內容均適用於對稱和反自反關係。這意味着:

  • 如果一個有關b,然後 b有關一個

  • 一個從未涉及一個

+0

這種方法在實踐中運行良好。非常感謝你。 – GloryFish 2009-02-02 16:34:36

1

我認爲你建議的結構很好。

要獲得相關的記錄做一些像

SELECT related.* FROM entities AS search 
LEFT JOIN entity_entity map ON map.entity_id_a = search.id 
LEFT JOIN entities AS related ON map.entity_id_b = related.id 
WHERE search.name = 'Search term' 

希望有所幫助。

+0

如果我的搜索詞,其ID只發生在entity_id_b在實體匹配地圖? – 2009-01-23 20:49:10

+0

換句話說,只有每個關係存儲兩次,相反,您的查詢纔有效。例如。 (1,4)和(4,1)。 – 2009-01-23 20:50:49

0
select * from entities 
where entity_id in 
(
    select entity_id_b 
    from entity_entity 
    where entity_id_a = @lookup_value 
) 
0

我能想到幾個方法。

單次使用CASE:

SELECT DISTINCT 
    CASE 
     WHEN entity_id_a <> @entity_id THEN entity_id_a 
     WHEN entity_id_b <> @entity_id THEN entity_id_b 
    END AS equivalent_entity 
FROM entity_entity 
WHERE entity_id_a = @entity_id OR entity_id_b = @entity_id 

或兩個過濾查詢被聯合這樣的:

SELECT entity_id_b AS equivalent_entity 
FROM entity_entity 
WHERE entity_id_a = @entity_id 
UNION 
SELECT entity_id_a AS equivalent_entity 
FROM entity_entity 
WHERE entity_id_b = @entity_id 
1

鏈接表方法似乎不錯,但您可能需要一個「關係型」,因此你知道他們爲什麼相關。

例如,羅利和北卡羅來納州之間的關係與羅利和達勒姆之間的關係並不相同。此外,如果您駕駛有條件的下拉列表,您可能想知道關係中的「父母」是誰。 (即你選擇一個國家,你會看到在該州的城市)。

根據您的要求的複雜性,您現在的簡單設置可能不夠。如果您只需要顯示兩條記錄以某種方式相關,則鏈接表應該足夠。

+0

我明白你的意思了。在這種情況下,我們特別不表示層次結構。在這個系統中只會有一個狀態,這些關係不會被用於下鑽式導航。 – GloryFish 2009-01-23 19:42:11

1

我已經在您的設計中發佈了一種方法,但如果您在設計中有一定的靈活性,並且這更貼近您的需求,我也希望提供這種獨立的設計見解。

如果項目在(非重疊)等價類中,您可能希望將等價類作爲表設計的基礎,其中類中的所有內容都被認爲是等價的。類本身也可以是匿名的:

CREATE TABLE equivalence_class (
    class_id int -- surrogate, IDENTITY, autonumber, etc. 
    ,entity_id int 
) 

entity_id應該是你的空間的非重疊的分區唯一的。

這避免了確保正確的左手或右手或強制右上關係矩陣的問題。

然後將查詢有一點不同:

SELECT c2.entity_id 
FROM equivalence_class c1 
INNER JOIN equivalence_class c2 
    ON c1.entity_id = @entity_id 
    AND c1.class_id = c2.class_id 
    AND c2.entity_id <> @entity_id 

或等價:

SELECT c2.entity_id 
FROM equivalence_class c1 
INNER JOIN equivalence_class c2 
    ON c1.entity_id = @entity_id 
    AND c1.class_id = c2.class_id 
    AND c2.entity_id <> c1.entity_id 
-1

我的建議是,你INTIAL表的設計是壞的。不要在同一個表中存儲不同類型的東西。 (數據庫設計的第一條規則,就是不要在同一個字段中存儲多條信息)。這很難進行查詢,並會導致重大性能問題。另外,將數據輸入到關係表中會出現問題 - 您如何知道當您執行新條目時需要實體化哪些實體?正確設計關係表格會好得多。實體表幾乎總是一個壞主意。在這個例子中我根本沒有理由在一張表中包含這種類型的信息。坦率地說,我有一張大學表和一張相關的地址表。這很容易查詢和執行得更好。

0

基於更新後的架構此查詢應該工作:

select if(entity_id_a=:entity_id,entity_id_b,entity_id_a) as related_entity_id where :entity_id in (entity_id_a, entity_id_b) 

其中:ENTITY_ID勢必要查詢的實體

相關問題