在同一张桌子上两次联接的最佳方法是什么?


108

这有点复杂,但是我有2个表。假设结构是这样的:

*Table1*
ID
PhoneNumber1
PhoneNumber2

*Table2*
PhoneNumber
SomeOtherField

可以基于Table1.PhoneNumber1-> Table2.PhoneNumber或Table1.PhoneNumber2-> Table2.PhoneNumber联接表。

现在,我想获得一个包含PhoneNumber1,与PhoneNumber1,PhoneNumber2对应的SomeOtherField和与PhoneNumber2对应的SomeOtherField的结果集。

我想到了两种方法来执行此操作-通过在表上联接两次或通过在ON子句中使用OR联接一次。

方法1

SELECT t1.PhoneNumber1, t1.PhoneNumber2, 
   t2.SomeOtherFieldForPhone1, t3.someOtherFieldForPhone2
FROM Table1 t1
INNER JOIN Table2 t2
   ON t2.PhoneNumber = t1.PhoneNumber1
INNER JOIN Table2 t3
   ON t3.PhoneNumber = t1.PhoneNumber2

这似乎有效。

方法2

某种程度上看起来像这样的查询-

SELECT ...
FROM Table1
INNER JOIN Table2 
   ON Table1.PhoneNumber1 = Table2.PhoneNumber OR
      Table1.PhoneNumber2 = Table2.PhoneNumber

我还没有做到这一点,我不确定是否有办法做到这一点。

做到这一点的最佳方法是什么?这两种方法似乎都不是简单或直观的。一般如何实施此要求?

Answers:


151

首先,我将尝试重构这些表,以免使用电话号码作为自然键。我不喜欢自然键,这是一个很好的例子。自然键,尤其是电话号码之类的东西,可以经常更改。当更改发生时更新数据库将是一个巨大的,容易出错的麻烦。*

正如您所描述的,方法1是您最好的选择。由于命名方案和简短的别名,它看起来有点简洁,但是...在多次连接同一张表或使用子查询等方面,别名是您的朋友。

我只是整理一下:

SELECT t.PhoneNumber1, t.PhoneNumber2, 
   t1.SomeOtherFieldForPhone1, t2.someOtherFieldForPhone2
FROM Table1 t
JOIN Table2 t1 ON t1.PhoneNumber = t.PhoneNumber1
JOIN Table2 t2 ON t2.PhoneNumber = t.PhoneNumber2

我做了什么:

  • 无需指定INNER-您未指定LEFT或RIGHT的事实暗示
  • 不要在主要查询表后缀n
  • N-后缀您将多次使用的表别名

* DBA避免麻烦的自然键更新方法之一是不指定主键和外键约束,这进一步加剧了数据库设计不良的问题。实际上,我经常看到这种情况。


我只是将这种解决方案用于自己的问题。这很有帮助。但是,在看到此信息之前,我在任何可以看到需要相互连接的表的地方都应用了主键和外键。为什么这是个坏主意?

6
@volumeone-我想您可能误解了我答案的最后一部分。主键和外键一个好主意。避免使用它们是不好的做法,糟糕的设计,甚至是很糟糕的。
Paul Sasik 2014年

完美。但是在这种情况下为什么必须使用别名?
Raiden Core

有没有一种方法可以在不两次连接同一张表的情况下执行此操作?也许在where子句中使用条件...
JohnOsborne

5

除非Phone1或(更可能是)phone2可以为null,否则第一个是好的。在这种情况下,您要使用左联接而不是内部联接。

当您有一个包含两个电话号码字段的表时,通常这是一个不好的信号。通常,这意味着您的数据库设计存在缺陷。


好点!以后,这会让我头疼。谢谢!
弗罗蒂(Froadie)2010年

4

您可以使用UNION合并两个联接:

SELECT Table1.PhoneNumber1 as PhoneNumber, Table2.SomeOtherField as OtherField
  FROM Table1
  JOIN Table2
    ON Table1.PhoneNumber1 = Table2.PhoneNumber
 UNION
SELECT Table1.PhoneNumber2 as PhoneNumber, Table2.SomeOtherField as OtherField
  FROM Table1
  JOIN Table2
    ON Table1.PhoneNumber2 = Table2.PhoneNumber

1
我想到了这一点,但我需要将它作为单个非规范化记录退还……
froadie 2010年

哦,好吧,我想的恰恰相反。如果是这种情况,那么我将使用您的第一种方法进行处理。我将编辑答案。
Pointy 2010年

3

我的问题是即使没有电话号码或只有一个电话号码(完整的通讯录)也要显示记录。因此,我使用了一个LEFT JOIN,即使左边没有对应的记录,它也会从左边获取所有记录。对我来说,这在Microsoft Access SQL中有效(它们需要括号!)

SELECT t.PhoneNumber1, t.PhoneNumber2, t.PhoneNumber3
   t1.SomeOtherFieldForPhone1, t2.someOtherFieldForPhone2, t3.someOtherFieldForPhone3
FROM 
(
 (
  Table1 AS t LEFT JOIN Table2 AS t3 ON t.PhoneNumber3 = t3.PhoneNumber
 )
 LEFT JOIN Table2 AS t2 ON t.PhoneNumber2 = t2.PhoneNumber
)
LEFT JOIN Table2 AS t1 ON t.PhoneNumber1 = t1.PhoneNumber;

2

第一种方法是正确的方法,它将满足您的需求。但是,对于内部联接,只有Table1两个电话号码都存在于中时,您才选择行Table2。您可能想要执行一个操作,LEFT JOIN以便Table1选中所有来自的行。如果电话号码不匹配,则SomeOtherFields为空。如果您要确保至少有一个匹配的电话号码,可以这样做WHERE t2.PhoneNumber IS NOT NULL OR t3.PhoneNumber IS NOT NULL

第二种方法可能会有问题:如果同时Table2具有PhoneNumber1和,会发生什么PhoneNumber2?将选择哪一行?根据您的数据,外键等,这可能是问题,也可能不是问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.