两次加入同一张桌子的最佳方法是什么?

时间:2010-11-24 14:35:06

标签: sql join

这有点复杂,但我有2张桌子。假设结构是这样的:

*Table1*
ID
PhoneNumber1
PhoneNumber2

*Table2*
PhoneNumber
SomeOtherField

可以根据Table1.PhoneNumber1 - >连接表格。 Table2.PhoneNumber,或Table1.PhoneNumber2 - > Table2.PhoneNumber。

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

我想到了两种方法 - 通过两次加入表格,或者在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

我还没有让它工作,我不确定是否有办法。

实现这一目标的最佳方法是什么?这两种方式都不简单或直观......有没有更简单的方法来做到这一点?这个要求一般如何实施?

6 个答案:

答案 0 :(得分:131)

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

方法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-Suffix您将多次使用的表别名,以使其显而易见

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

答案 1 :(得分:4)

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

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

答案 2 :(得分:2)

第一种方法是正确的方法,并将做你需要的。但是,对于内部联接,如果Table1中存在两个电话号码,则只会从Table2中选择行。您可能需要执行LEFT JOIN,以便选择Table1中的所有行。如果电话号码不匹配,则SomeOtherField将为空。如果您想确保至少有一个匹配的电话号码,则可以WHERE t2.PhoneNumber IS NOT NULL OR t3.PhoneNumber IS NOT NULL

第二种方法可能会出现问题:如果Table2同时包含PhoneNumber1PhoneNumber2会怎样?选择哪一行?根据您的数据,外键等,这可能是也可能不是问题。

答案 3 :(得分:2)

您可以使用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

答案 4 :(得分:2)

我的问题是显示记录,即使没有或只有一个电话号码(完整地址簿)。因此,我使用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;

答案 5 :(得分:0)

  const { API } = withSSRContext(context)
  let movieData
  try {
    movieData = await API.graphql({
      query: listMovies,
      authMode: "AMAZON_COGNITO_USER_POOLS"
    });
    console.log('movieData: ', movieData)
  } catch (err) {
    console.log("error fetching movies: ", err)
  }
  return {
    props: {
      movies: movieData ? movieData.data.listMovies.items : null
    }
  }
}```

SELECT T1.ID T1.PhoneNumber1, T1.PhoneNumber2 T2A.SomeOtherField AS "SomeOtherField of PhoneNumber1", T2B.SomeOtherField AS "SomeOtherField of PhoneNumber2" FROM Table1 T1 LEFT JOIN Table2 T2A ON T1.PhoneNumber1 = T2A.PhoneNumber LEFT JOIN Table2 T2B ON T1.PhoneNumber2 = T2B.PhoneNumber WHERE T1.ID = 'FOO'; LEFT JOIN 也返回相同的结果。使用 PostgreSQL 13.1.1 测试成功。

enter image description here