多列上的相同联接约束

San*_*eep 4 mysql join mysql-5.6

我们可以使用以下简单示例重新创建一种情况。我有以下两个示例表:

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

  PRIMARY KEY(id)  
);  
Run Code Online (Sandbox Code Playgroud)

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

  PRIMARY KEY(id)  
);  
Run Code Online (Sandbox Code Playgroud)

我按如下方式填充它们:

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");
Run Code Online (Sandbox Code Playgroud)

显然,表的内容如下:

+----+--------------+--------------+----------+
| 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 |
+----+-----------+-------------+
Run Code Online (Sandbox Code Playgroud)

我们想执行一个 JOIN 操作,以便我们得到这样的输出:

+-------------+-------------+--------+
| John Doe    | Jose Garcia | Team A |
+-------------+-------------+--------+
| Larry Brown | Mary Smith  | Team B |
+-------------+-------------+--------+
Run Code Online (Sandbox Code Playgroud)

priContactIdsecContactId列的连接约束是相同的,我很难弄清楚 JOIN 查询应该是什么样子。

仅供参考,我们使用的是 MySQL 版本5.6.49

Vér*_*ace 5

这是一个有趣的例子,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)  
);
Run Code Online (Sandbox Code Playgroud)

填充它们:

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

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");
Run Code Online (Sandbox Code Playgroud)

请注意具有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;
Run Code Online (Sandbox Code Playgroud)

结果:

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
Run Code Online (Sandbox Code Playgroud)

因此,现在我们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;
Run Code Online (Sandbox Code Playgroud)

结果:

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
Run Code Online (Sandbox Code Playgroud)

因此,获得我们的结果后,我们现在可以按如下方式清理 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;
Run Code Online (Sandbox Code Playgroud)

结果:

Con_1 name     Con_2 name     Team
  John Doe     Jose Garcia  Team A
Larry Brown    Mary Smith   Team B
Run Code Online (Sandbox Code Playgroud)

Et voilà - 结果如愿!

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
Run Code Online (Sandbox Code Playgroud)

所以,现在,你必须这样使用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;
Run Code Online (Sandbox Code Playgroud)

结果:

  Con_1 name      Con_2 name      Team
    John Doe     Jose Garcia    Team A
    John Doe            NULL    Team B
 Larry Brown      Mary Smith    Team B
Run Code Online (Sandbox Code Playgroud)

因此,现在带有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 更具可读性和可移植性!