假设我想显示公司列表,以及该公司的第一位/随机员工。
我可以这样做:
SELECT
company.id,
company.name,
MIN(person.id) AS employee_person_id,
MIN(person.name) AS employee_person_name
FROM company
LEFT OUTER JOIN person ON (person.company_id = company.id)
GROUP BY company.id;
但我认为使用上面的代码,
MIN(person.id)
和 MIN(person.name)
可以提供有关两个不同人的信息,对吗?
是否有更好的方法来检索“第一个”(或随机的)员工并显示该人的 ID 和姓名?
我会使用
row_number
窗口函数在每个公司内分配一个编号,然后用它来查询第一个人:
SELECT c_id, c_name, p_id, p_name
FROM (SELECT c.id AS c_id,
c.name AS c_name,
p.id AS p_id,
p.name AS p_name,
ROW_NUMBER() OVER (PARTITION BY c.id ORDER BY p.id ASC) AS rn
FROM company c
LEFT JOIN person p ON p.company_id = c.id) t
WHERE rn = 1
在 Postgres 中,我会推荐
distinct on
:
SELECT distinct on (c.id)
c.id,
c.name,
p.id AS employee_person_id,
p.name AS employee_person_name
FROM company c
LEFT OUTER JOIN person p ON p.company_id = c.id
ORDER BY c.id, p.id
对于每家公司,这带来了id最小的人;您可以使用
ORDER BY
子句控制选择哪个人(如果您想要拥有最大 id 的人,您可以使用 ORDER BY c.id, p.id DESC
)。
这是获得正确名称的方法:
您必须通过
person.id = s.employee_person_id
将您的结果加入人员表
select s.company_id, s.company_name, s.employee_person_id, p.name as employee_person_name
from person p
inner join (
SELECT
company.id as company_id,
company.name as company_name,
MIN(person.id) AS employee_person_id
FROM company
LEFT OUTER JOIN person ON (person.company_id = company.id)
GROUP BY company.id
) as s on s.employee_person_id = p.id
这是正确的:
MIN(person.id) 和 MIN(person.name) 可以提供关于两个不同人的信息,对吗?
您可以在下面链接的演示中看到这种情况。
GMB 的
distinct on
通常是最受推荐的选择,但它需要订购,与 Mureinik 的一样。同时,您可以让each公司只需获得any singleperson
,而无需先订购:demo
SELECT
company.id,
company.name,
person.id,
person.name
FROM company LEFT JOIN LATERAL
(SELECT id,name FROM person WHERE company_id=company.id LIMIT 1) person
ON true;
它是只检索“第一个”(或随机的)员工的便捷方式:它会先找到它,而不必在选择一个之前找到并排序所有可能的匹配项(甚至是快速选择)。每家公司都只是从他们的员工中提取任何一个,这似乎是个主意。
由于没有做额外的工作,速度更快(在 300'000 行样本上为 16'000x) 并且它仅与
company
成比例缩放,几乎忽略了 person
表的大小和增长。