需要帮助在Oracle中创建正确的联接

时间:2019-09-17 16:41:26

标签: sql oracle join

我是一名前端开发人员,负责创建一个查询,该查询将为存储在我们数据库中的满足特定条件的用户提取联系人数据(电子邮件)。有些用户没有电子邮件地址,但无论如何我都希望显示其剩余信息。我读过关于Oracle Joins的Techonthenet article,但似乎没有得到。下面是添加联接之前的代码。

SELECT p.person_id, p.first_name || ' ' || p.last_name person_name, pci.contact_data
 FROM cse.meeting m, cse.person p, cse.meeting_person mp, cse.person_contact_info pci
WHERE mp.meeting_id = m.meeting_id
  AND mp.person_id = p.person_id
  AND mp.person_id = pci.person_id
  AND m.meeting_date >= '03-Jan-2020'
  AND pci.contact_type_code = 'EMAIL'
ORDER BY m.session_date ASC;

我都尝试过两者:

AND mp.person_id = pci.person_id(+)

AND mp.person_id(+) = pci.person_id

没有成功。我还尝试使用诸如此类的显式代码来完成此操作:

SELECT p.person_id, p.first_name || ' ' || p.last_name person_name, pci.contact_data
 FROM cse.meeting m, cse.person p, cse.meeting_person mp LEFT JOIN cse.person_contact_info pci ON mp.person_id = pci.person_id
WHERE mp.meeting_id = m.meeting_id
  AND mp.person_id = p.person_id
  AND m.meeting_date >= '03-Jan-2020'
  AND pci.contact_type_code = 'EMAIL'
ORDER BY m.session_date ASC;

仍然没有运气。对于表person_contact_info中没有一行且contact_type_code为“ EMAIL”的人,它不会返回数据。有人可以向我描述我在做什么错吗? “加入”的概念让我有些头疼。

谢谢

3 个答案:

答案 0 :(得分:4)

您有一个外部联接条件,但是随后您也引用了pci子句中的where表-这具有将其变回内部联接的作用。将所有外部联接的条件放在其on子句中,即使该条件似乎并不完全属于该条件:

LEFT JOIN cse.person_contact_info pci ON mp.person_id = pci.person_id
AND pci.contact_type_code = 'EMAIL'

因此,还要替换旧式连接以保持一致:

SELECT p.person_id, p.first_name || ' ' || p.last_name person_name, pci.contact_data
FROM cse.meeting m
JOIN cse.meeting_person mp ON mp.meeting_id = m.meeting_id
JOIN cse.person p ON p.person_id = mp.person_id
LEFT JOIN cse.person_contact_info pci ON pci.person_id = p.person_id
AND pci.contact_type_code = 'EMAIL'
WHERE m.meeting_date >= DATE '2020-01-03'
ORDER BY m.session_date ASC;

使用单一样式并不是为了保持一致性而。首先评估新式联接,因此使用混合引用的更复杂查询会出现奇怪的错误。

我还使用a date literal作为目标日期;您正在与依赖于隐式转换的字符串进行比较,如果运行此字符串的NLS设置与您不同,则该字符串将失败。


旧式外部联接的问题相同,只是看起来有所不同。您有pci的外部和内部引用:

AND mp.person_id = pci.person_id(+)
AND pci.contact_type_code = 'EMAIL'

因此,如果您必须以这种样式进行操作,则需要将两个部分都设为外部(也许可以通过交换术语来使其更容易遵循):

AND pci.person_id (+) = mp.person_id
AND pci.contact_type_code (+) = 'EMAIL'

除非您有充分的理由,否则无论如何都应使用新型联接。 “新”在这里是指1992年,所以当然不是真正的“新”。

答案 1 :(得分:1)

您想显示在​​2020年1月3日至少分配给一次会议的所有人员。因此,从表person中选择。

您将在IN子句中使用EXISTSWHERE子句来指定条件,但是您想按会议的会议日期进行排序,因此必须加入会议表代替。 (这有点奇怪,因为在按会话日期排序时,您没有显示它,从而使查询结果看起来是无序的。)

然后,如果此人收到电子邮件,则要显示电子邮件。因此,外部联接表person_contact_info

以下是查询:

SELECT
  p.person_id, 
  p.first_name || ' ' || p.last_name AS person_name,
  pci.contact_data
 FROM cse.person p
 JOIN cse.meeting_person mp ON mp.person_id = p.person_id
 JOIN cse.meeting m ON m.meeting_id = mp.meeting_id
                   AND m.meeting_date >= DATE '2020-01-03'
 LEFT JOIN cse.person_contact_info pci ON pci.person_id = p.person_id
                                      AND pci.contact_type_code = 'EMAIL'
ORDER BY m.session_date;

(另一件事:您要在2020年1月3日与某人一起参加会议。如果某人当天有3次会议,您将向该人展示三倍。所以,也许您确实想使用INEXISTS。)

答案 2 :(得分:0)

我认为我看到了问题。如果您为cse.person_contact_info别名的pci中不存在该记录,则字段pci.contact_type_codeNULL。这是因为如果LEFT JOIN在联接表中找不到,则用NULL填充该字段。

因此,当您在pci.contact_type_code = 'EMAIL'子句中调用WHERE时,它与未找到的记录不匹配,因为该字段在{{1 }}。

要对此进行解释,请将您的where子句中的这一行更改:NULLJOIN,它将接受AND pci.contact_type_code = 'EMAIL'AND (pci.contact_type_code = 'EMAIL' OR pci.contact_type_code IS NULL).或找不到{{{ 1}})。