Mysql

多列上的相同連接約束

  • December 8, 2020

我們有一種情況,我可以通過以下簡單範例重新創建。我有以下兩個範例表:

CREATE TABLE contact_info
(
 id INT UNSIGNED AUTO_INCREMENT,
 priContactId INT,
 secContactId INT,
 blahBlah VARCHAR(32),

 PRIMARY KEY(id)  
);  

CREATE TABLE name_lookup
(
 id INT UNSIGNED AUTO_INCREMENT,
 contactID INT,
 contactName VARCHAR(32),

 PRIMARY KEY(id)  
);  

我將它們填充如下:

INSERT INTO contact_info(priContactId, secContactId, blahBlah) VALUES(1, 3, "Team A"), (4, 2, "Team B");
INSERT INTO name_lookup(contactID, contactName) VALUES(1, "John Doe"), (2, "Mary Smith"), (3, "Jose Garcia"), (4, "Larry Brown");

顯然,表格的內容如下:

+----+--------------+--------------+----------+
| id | priContactId | secContactId | blahBlah |
+----+--------------+--------------+----------+
|  1 |            1 |            3 | Team A   |
|  2 |            4 |            2 | Team B   |
+----+--------------+--------------+----------+

+----+-----------+-------------+
| id | contactID | contactName |
+----+-----------+-------------+
|  1 |         1 | John Doe    |
|  2 |         2 | Mary Smith  |
|  3 |         3 | Jose Garcia |
|  4 |         4 | Larry Brown |
+----+-----------+-------------+

我們想執行一個 JOIN 操作,以便我們得到這樣的輸出:

+-------------+-------------+--------+
| John Doe    | Jose Garcia | Team A |
+-------------+-------------+--------+
| Larry Brown | Mary Smith  | Team B |
+-------------+-------------+--------+

priContactId和列的連接約束secContactId是相同的,我很難弄清楚 JOIN 查詢應該是什麼樣子。

僅供參考,我們使用的是 MySQL 版本5.6.49

這是一個有趣的例子,SELF-JOIN證明(間接)s 是有用的!

為了回答您的問題,我執行了以下操作(以下所有 SQL 都可以在此處的小提琴中找到):

我使用了問題中提供的您的 DDL 和 DML - 謝謝(以及 +1 - 您只問了兩個問題,所以我認為您是新貢獻者,很高興看到有些人不厭其煩地提供 DDL 和 DML - 如果只有所有 OP 都這樣做!)。

CREATE TABLE name_lookup
(
 id INT UNSIGNED AUTO_INCREMENT,
 contact_id INT,
 contact_name VARCHAR(32),

 PRIMARY KEY(id)  
);



CREATE TABLE contact_info
(
 id INT UNSIGNED AUTO_INCREMENT,
 pri_contact_id INT,
 sec_contact_id INT,
 blah_blah VARCHAR(32),

 PRIMARY KEY(id)  
);

填充它們:

INSERT INTO name_lookup(contact_id, contact_name) 
VALUES
(1, "John Doe"), (2, "Mary Smith"), 
(3, "Jose Garcia"), (4, "Larry Brown");

INSERT INTO contact_info(pri_contact_id, sec_contact_id, blah_blah) 
VALUES(1, 3, "Team A"), (4, 2, "Team B"), (1, NULL, "Team A");

請注意具有sec_contact_id= NULL- 的最終記錄,請參閱**EDIT**下面的討論。我隱含地假設團隊身份是由pri_contact_id- 必要時定義的。

您還會注意到我使用snake_caselower_case_with_underscores- 個人喜好 - 選擇一種風格並堅持下去!.

我在小提琴的 SQL 中留下了額外的欄位,以便您可以看到所涉及的思維過程以及我是如何得出解決方案的!

SELECT 
 nl1.id, nl1.contact_id, nl1.contact_name, 
 ci1.pri_contact_id, ci1.sec_contact_id, ci1.blah_blah
FROM name_lookup nl1
JOIN contact_info ci1 
 ON nl1.contact_id = ci1.pri_contact_id;

結果:

id  contact_id  contact_name    pri_contact_id  sec_contact_id  blah_blah
1           1      John Doe                 1               3     Team A
4           4   Larry Brown                 4               2     Team B

因此,現在我們使用它們之間的連結name_lookup重新加入自身。contact_info

SELECT 
 nl1.id, nl1.contact_id, nl1.contact_name,
 nl2.id, nl2.contact_id, nl2.contact_name,
 ci1.pri_contact_id, ci1.sec_contact_id, ci1.blah_blah
FROM name_lookup nl1
JOIN contact_info ci1 
 ON nl1.contact_id = ci1.pri_contact_id
JOIN name_lookup nl2
 ON ci1.sec_contact_id = nl2.contact_id
ORDER BY nl1.id;

結果:

id  contact_id  contact_name    id  contact_id  contact_name    pri_contact_id  sec_contact_id  blah_blah
1           1      John Doe     3           3   Jose Garcia                 1   3    Team A
4           4   Larry Brown     2           2    Mary Smith                 4 2      Team B

因此,在獲得結果後,我們現在可以清理 SQL(僅SELECT必填欄位 - 減少任何網路流量以及伺服器上的 I/O),如下所示:

SELECT 
 nl1.contact_name AS "Con_1 name",
 nl2.contact_name AS "Con_2 name",
 ci1.blah_blah AS "Team"
FROM name_lookup nl1
JOIN contact_info ci1 
 ON nl1.contact_id = ci1.pri_contact_id
JOIN name_lookup nl2
 ON ci1.sec_contact_id = nl2.contact_id
ORDER BY nl1.id;

結果:

Con_1 name     Con_2 name     Team
 John Doe     Jose Garcia  Team A
Larry Brown    Mary Smith   Team B

等等 - 結果如願!

EDIT (NULLs in sec_contact_id):

有人向我指出,我的回答並不像我希望的那樣全面。萬一哪一個畢竟sec_contact_idNULL可能的——你可能已經做了第一個,但後續工作還沒有完成?

因此,我稍微更改了表格,它現在包含(如您將在上面看到的 -這裡有一個相當大的變化的小提琴- 我也想在 PostgreSQL 上執行它):

Con_1 name   Con_2 name      Team
  John Doe  Jose Garcia    Team A
  John Doe         NULL    Team B
Larry Brown   Mary Smith    Team B

所以,現在,你必須使用INNER JOINs :

 nl1.contact_name AS "Con_1 name",
 nl2.contact_name AS "Con_2 name",
 ci1.blah_blah AS "Team"
FROM contact_info ci1
   LEFT JOIN name_lookup nl1
       ON nl1.contact_id = ci1.pri_contact_id
   LEFT JOIN name_lookup nl2
       ON nl2.contact_id = ci1.sec_contact_id
ORDER BY nl1.id, ci1.blah_blah;

結果:

 Con_1 name      Con_2 name      Team
   John Doe     Jose Garcia    Team A
   John Doe            NULL    Team B
Larry Brown      Mary Smith    Team B

因此,現在帶有sec_contact_id = NULL基準的記錄出現在您的結果集中。

幾句忠告:

  • 你真的應該考慮(強烈)考慮從 5.6 升級到 MySQL 8 的目前版本——你會得到視窗函式、生成的列、CHECK約束——現在是 22 版,我沒有聽到很多抱怨,所以它會是一個不錯的選擇!
  • 許多人認為NULLs 是不受歡迎的,並像瘟疫一樣避免它們——我傾向於屬於這一類。因此,您可能希望考慮擁有兩個聯繫人表 -pri_contact_infosec_contact_info. 您可以決定是否要這樣做並提出一個新問題 - 如果您這樣做,也請在這裡告訴我!
  • 您可以考慮回答這個問題(在您的原始文章下方發表評論)Can there ever be more than two people on a team? I e. There would be 3 columns with team members names for the same row?:!
  • 表的應該是PRIMARY KEY-代理沒有添加任何東西!我假設在僱用員工時或多或少地隨機分配給他們?所以,它本質上是一個代理鍵本身。代理鍵有它們的位置,但有時它們不是要走的路!name_lookup``contact_id id``contact_id
  • 在標準支持方面,MySQL 無疑是主要 RDBMS 中最差的,而且它還有大量的非標準"extensions"——將來,也許你可以用撇號'而不是雙引號來分隔你的 SQL 字元串"?我個人**suggestion**是,您使用雙引號作為欄位名稱的別名作為最終結果(表示,而不是內容),就像我所做的那樣 - 這將使您的 SQL 更具可讀性和可移植性!

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