在LEFT JOIN中防止重复值

时间:2015-05-23 08:32:52

标签: sql join

我遇到了LEFT JOIN重复值的情况。我认为这可能是一种理想的行为,但与我想要的不同。

我有三个表: person department contact

人:

id bigint,
person_name character varying(255)

部门:

person_id bigint,
department_name character varying(255)

联系方式:

person_id bigint,
phone_number character varying(255)

Sql Query:

SELECT p.id, p.person_name, d.department_name, c.phone_number 
FROM person p
  LEFT JOIN department d 
    ON p.id = d.person_id
  LEFT JOIN contact c 
    ON p.id = c.person_id;

结果:

id|person_name|department_name|phone_number
--+-----------+---------------+------------
1 |"John"     |"Finance"      |"023451"
1 |"John"     |"Finance"      |"99478"
1 |"John"     |"Finance"      |"67890"
1 |"John"     |"Marketing"    |"023451"
1 |"John"     |"Marketing"    |"99478"
1 |"John"     |"Marketing"    |"67890"
2 |"Barbara"  |"Finance"      |""
3 |"Michelle" |""             |"005634"

我知道这是联接所做的事情,保持与所选行相乘。但它给出的感觉就像电话号码0234519947867890适用于两个部门,而它们只与人员john有关,其中包含不必要的重复值,这会使更大的数据集升级问题。
所以,这就是我想要的:

id|person_name|department_name|phone_number
--+-----------+---------------+------------
1 |"John"     |"Finance"      |"023451"
1 |"John"     |"Marketing"    |"99478"
1 |"John"     |""             |"67890"
2 |"Barbara"  |"Finance"      |""
3 |"Michelle" |""             |"005634"

这是我的情况的一个示例,我正在使用大量的表和查询。所以,需要一种通用的解决方案。

5 个答案:

答案 0 :(得分:12)

我喜欢将此问题称为“通过代理交叉加入”。由于没有信息(WHEREJOIN条件)表departmentcontact如何匹配,因此它们通过代理表{{交叉连接1}} - 给你Cartesian product。与此非常相似:

那里有更多解释。

您的查询解决方案:

person

您没有定义要选择的部门或电话号码,因此我随意选择第一个。你可以用任何其他方式......

答案 1 :(得分:2)

我认为您只需要获取特定人员的部门和电话列表。所以只需使用array_agg(或string_aggjson_agg):

SELECT
    p.id,
    p.person_name,
    array_agg(d.department_name) as "department_names",
    array_agg(c.phone_number) as "phone_numbers"
FROM person AS p
LEFT JOIN department AS d ON p.id = d.person_id
LEFT JOIN contact AS c on p.id = c.person_id
GROUP BY p.id, p.person_name

答案 2 :(得分:1)

虽然表格显然已经简化讨论,但看起来它们在结构上存在缺陷。表的结构应该显示实体之间的关系,而不仅仅是实体和/或属性的列表。在这种情况下,我会将电话号码视为(个人或部门实体)的属性。

第一步是创建具有关系的表,每个表都有一个主键,可能还有一个外键。在此示例中,让person表使用person_id作为主键,department表使用department_id作为其主键会很有帮助。接下来查看一对多或多对多关系,并相应地设置外键:

  • 如果一个人一次只能在一个部门,那么你就有一个(部门)对多人(人)。 department表中没有外键,但department_id将是人员表中的外键。
  • 如果一个人可以在多个部门,他们就有多对多,你需要一个额外的联结表,其中person_id和department_id作为外键。

总而言之,您的方案中应该只有两个表:一个表用于人员,另一个表用于部门。即使允许个人电话号码(人员表中的列)和部门表中的部门编号,这也是更好的方法。

唯一需要注意的是,一个部门有多个号码(或多个部门共用一个电话号码),但这超出了原始问题的范围。

答案 3 :(得分:0)

使用此类查询: SQL Server
(您可以将id的{​​{1}}更改为您想要的每个列

ORDER BY id

答案 4 :(得分:0)

SELECT p.id, p.person_name, d.department_name, c.phone_number 
FROM person p
  LEFT JOIN department d 
    ON p.id = d.person_id
  LEFT JOIN contact c 
    ON p.id = c.person_id 
group by p.id, p.person_name, d.department_name, c.phone_number