Join

從橋接表的任一側選擇行的正確方法是什麼?

  • June 8, 2019

我想對三列進行 SQL 查詢。數據庫有五個表,其中兩個是另外三個的橋表。當我嘗試從表中進行選擇時,我得到了大量不一定具有相關列的行,這對我來說是個問題。我的問題是:如何從三個表中選擇相關列,這些表的行通過橋表關聯?例如:我有通過橋接錶鍊接的學生、班級和教師表。我想從這些表格中選擇學生、他們的班級以及教授這些課程的人。

實際上,我有三個表:itemstagscategorys以及兩個連接它們的橋表:itemTagBridgetagCategoryBridge。這個想法是,items表中的項目可以被賦予一個或多個儲存在tags表中的標籤,並且可以為標籤分配一個或多個儲存在表中的類別categorys。該itemTagBridge表具有它正在加入的項目和標籤的tagCategoryBridge列,並且該表具有它正在加入的標籤和類別的列。我想從數據庫中選擇分配給這些標籤的項目、標籤和類別。我正在使用 SQLite3。我暫時用來解決這個問題的程序叫做 SQLite Studio。數據庫結構的目前狀態如下所示:

items:
   id INTEGER PRIMARY KEY
   item TEXT

tags:
   id INTEGER PRIMARY KEY
   tag TEXT

categorys:
   id INTEGER PRIMARY KEY
   category TEXT

itemTagBridge:
   id INTEGER PRIMARY KEY
   itemId INTEGER FOREIGN KEY REFERENCES items (id)
   tagId INTEGER FOREIGN KEY REFERENCES tags (id)

tagCategoryBridge:
   id INTEGER PRIMARY KEY
   tagId INTEGER FOREIGN KEY REFERENCES tags (id)
   categoryId INTEGER FOREIGN KEY REFERENCES categorys (id)

我正在測試的數據如下:

items:
   1    "Hello World!"
   2    "Goodbye World!"

tags:
   1    "positivity"
   2    "hello"
   3    "negativity"
   4    "goodbye"
   5    "helloWorld"
   6    "goodByeWorld"

categorys:
   1    "keyword"
   2    "title"

itemTagBridge:
   1    1    1
   2    1    2
   3    1    5
   4    2    3
   5    2    4
   6    2    6

tagCategoryBridge:
   1    1    1
   2    2    1
   3    3    1
   4    4    1
   5    5    2
   6    6    2

理論上,我應該能夠通過將橋連接到它們各自的表中來從表項目、標籤和類別表中選擇項目、標籤和類別列,如下所示:

SELECT i.item, t.tag, c.category
FROM items as i, tags as t, categorys as c
INNER JOIN itemTagBridge AS it ON i.id = it.itemId -- Join 1st side of "it" bridge
INNER JOIN itemTagBridge ON t.id= it.tagId -- Join 2nd side of "it" bridge

INNER JOIN tagCategoryBridge AS tc ON  t.id = tc.tagId -- Join 1st side of "tc" bridge
INNER JOIN tagCategoryBridge ON c.id = tc.categoryId; -- Join 2nd side of "tc" bridge

我的問題是上面的 SQL 選擇了 216 行,而它應該選擇的行要少得多。使用時SELECT DISTINCT,選擇 6 行。使用時GROUP BY,也可以選擇最少 6 行。選擇的另一個問題是選擇的行沒有正確匹配(hello 不是標題,hello 是關鍵字,helloWorld 是標題)。

6行:

item:           tag:            category:
Goodbye World   goodByeWorld    keyword
Goodbye World   goodbye         keyword
Goodbye World   negativity      keyword
Hello World     helloWorld      keyword
Hello World     hello           title
Hello World     positivity      title

我一直認為我對連接有嚴重的誤解,不知道在這種情況下適合使用的 SQL 操作,需要使用不同的模式,需要使用不同的數據庫管理系統,或者完全遺漏了一些東西否則有這個問題。我的問題是如何從這個數據庫中選擇項目、標籤和類別,或者如果這是不可能的、非最佳的或不切實際的,那麼更好的解決方案是什麼?

感謝您的所有幫助,我是 SQL 新手,我已經在這裡迷路了大約一周,看不到盡頭。

編輯:

DDL

-- Drop tables
DROP TABLE IF EXISTS itemTagBridge;
DROP TABLE IF EXISTS tagCategoryBridge;

DROP TABLE IF EXISTS items;
DROP TABLE IF EXISTS tags;
DROP TABLE IF EXISTS categorys;


-- Create tables
CREATE TABLE items(
   id INTEGER PRIMARY KEY,
   item TEXT);

CREATE TABLE tags(
   id INTEGER PRIMARY KEY,
   tag TEXT);

CREATE TABLE categorys(
   id INTEGER PRIMARY KEY,
   category TEXT);


CREATE TABLE itemTagBridge(
   id INTEGER PRIMARY KEY,
   itemId INTEGER REFERENCES items (id),
   tagId INTEGER REFERENCES tags (id));

CREATE TABLE tagCategoryBridge(
   id INTEGER PRIMARY KEY,
   tagId INTEGER REFERENCES tags (id),
   categoryId INTEGER REFERENCES categorys (id));

DML

-- Insert test data into tables
INSERT INTO items (item)
VALUES ("Hello World!"), ("Goodbye World!");

INSERT INTO tags (tag)
VALUES ("positivity"), ("hello"), ("negativity"), ("goodbye"), ("helloWorld"), ("goodByeWorld");

INSERT INTO categorys (category)
VALUES ("keyword"), ("title");


INSERT INTO itemTagBridge (itemId, tagId)
Values (1, 1), (1, 2), (1, 5), (2, 3), (2, 4), (2, 6);

INSERT INTO tagCategoryBridge (tagId, categoryId)
VALUES (1, 1), (2, 1), (3, 1), (4, 1), (5, 2), (6, 2);

為了解決您的問題,我執行了以下操作:

建構了您的表格和數據,如這裡的小提琴所示。也顯示在這個答案的底部。

對於您的連接表(或關聯實體),我將兩個連接欄位組合在一起 - 由於此處此處列出的原因PRIMARY KEY,單獨是多餘的。(這也是我過去常犯的錯誤!:-))。 INTEGER PRIMARY KEY

在 SQLite 3.8 上,我執行了這個查詢(小提琴):

SELECT i.item, t.tag, c.category
FROM items i
JOIN itemTagBridge itb ON i.id = itb.itemID
JOIN tags t ON itb.tagID = t.id
JOIN tagCategoryBridge tcb ON itb.tagId = tcb.tagId
JOIN categorys c ON tcb.categoryId = c.id;

結果:

item            tag           category
Hello World!    positivity    keyword
Hello World!    hello         keyword
Hello World!    helloWorld    title
Goodbye World   negativity    keyword
Goodbye World   goodbye       keyword
Goodbye World   goodByeWorld  title
6 rows

這似乎是您想要的結果。

然後我執行了你自己的查詢(小提琴

SELECT i.item, t.tag, c.category
FROM items as i, tags as t, categorys as c
INNER JOIN itemTagBridge AS it ON i.id = it.itemId -- Join 1st side of "it" bridge
INNER JOIN itemTagBridge ON t.id= it.tagId -- Join 2nd side of "it" bridge

INNER JOIN tagCategoryBridge AS tc ON  t.id = tc.tagId -- Join 1st side of "tc" bridge
INNER JOIN tagCategoryBridge ON c.id = tc.categoryId; -- Join 2nd side of "tc" bridge

它確實產生了 216 個結果,6 組 36 個相同的結果。

36 = 6 * 6,這是您在加入表格時擁有的欄位數 - 所以您似乎CROSS JOIN在它們之間進行某種操作,並且 36 * 6 = 216,因此您似乎加入標籤 3 次(6 條記錄在標籤)與其他表和您的連接表都有標籤。

您要做的是通過連接表加入item到。您似乎加入了您的橋接表兩次,而不是僅一次,這導致了我們在您的 216 條記錄結果集中看到的乘法效應。tag``category

使用您的表結構,您要做的就是簡單地通過 id 連接,作為表之間的連結欄位。在這最後的小提琴中,我逐行剖析了我的查詢和您的查詢。過去我發現這是一種非常有效的學習方法。HTH,歡迎來到論壇!

================= 表的 DDL 和 DML ===================

將來,根據評論中的要求,請以 DDL 和 DML 的形式提供表格和數據 - 幫助我們為您提供幫助!我的個人資料上有一些關於在這個論壇上提問的文章 - 你可能想看看?

CREATE TABLE items
(
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   item TEXT
);

CREATE TABLE tags
(
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   tag TEXT
);

CREATE TABLE categorys
(
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   category TEXT
);

CREATE TABLE itemTagBridge
(
   itemId INTEGER,
   tagId INTEGER, 
   PRIMARY KEY (itemId, tagId),
   FOREIGN KEY (itemID) REFERENCES items (id),
   FOREIGN KEY (tagId)  REFERENCES tags (id)
);

CREATE TABLE tagCategoryBridge
(
   tagId INTEGER,
   categoryId INTEGER,  
   PRIMARY KEY (tagId, categoryId),
   FOREIGN KEY (tagId) REFERENCES tags (id),
   FOREIGN KEY (categoryId) REFERENCES categorys (id)
);

INSERT INTO items (item)
VALUES ('Hello World!'), ('Goodbye World');

INSERT INTO categorys (category) VALUES ('keyword'), ('title');

INSERT INTO tags (tag)
VALUES ('positivity'), ('hello'), ('negativity'), ('goodbye'), ('helloWorld'), ('goodByeWorld');

INSERT INTO itemTagBridge
VALUES(1, 1), (1, 2), (1, 5), (2, 3), (2, 4), (2, 6);

INSERT INTO tagCategoryBridge
VALUES (1, 1), (2, 1), (3, 1), (4, 1), (5, 2), (6, 2);

引用自:https://dba.stackexchange.com/questions/240112