这是我的疑问:
SELECT autor.entwickler,anwendung.name
FROM autor
left join anwendung
on anwendung.name = autor.anwendung;
entwickler | name
------------+-------------
Benutzer 1 | Anwendung 1
Benutzer 2 | Anwendung 1
Benutzer 2 | Anwendung 2
Benutzer 1 | Anwendung 3
Benutzer 1 | Anwendung 4
Benutzer 2 | Anwendung 4
(6 rows)
我想为字段name
中的每个不同值保留一行,并丢弃其他类似的行:
entwickler | name
------------+-------------
Benutzer 1 | Anwendung 1
Benutzer 2 | Anwendung 2
Benutzer 1 | Anwendung 3
Benutzer 1 | Anwendung 4
在MySQL中,我会这样做:
SELECT autor.entwickler,anwendung.name
FROM autor
left join anwendung
on anwendung.name = autor.anwendung
GROUP BY anwendung.name;
但是PostgreSQL给了我这个错误:
错误:列“autor.entwickler”必须出现在GROUP BY子句中 或者在聚合函数中使用LINE 1:SELECT autor.entwickler 从autor左边加入anwendung ......
我完全理解错误,并假设mysql实现比postgres实现更少SQL。但是我怎样才能得到理想的结果呢?
答案 0 :(得分:34)
PostgreSQL当前不允许含糊不清的GROUP BY
语句,其结果取决于扫描表的顺序,使用的计划等。标准表示它应该如何工作AFAIK,但有些数据库(如5.7之前的MySQL版本允许更宽松的查询,只选择SELECT
列表中出现的元素遇到的第一个值,而不是GROUP BY
中的元素。
在PostgreSQL中,您应该使用DISTINCT ON
进行此类查询。
你想写一些类似的东西:
SELECT DISTINCT ON (anwendung.name) anwendung.name, autor.entwickler
FROM author
left join anwendung on anwendung.name = autor.anwendung;
(根据后续评论纠正的语法)
这有点像MySQL 5.7的ANY_VALUE(...)
group by
伪函数,但相反 - 它表示distinct on
子句中的值必须是唯一的,任何未指定
除非有ORDER BY
,否则没有保证选择了哪些值。您通常应该有ORDER BY
的可预测性。
还注意到使用min()
或max()
这样的聚合可行。虽然这是正确的 - 并且将导致可靠和可预测的结果,与使用DISTINCT ON
或一个暧昧的GROUP BY
不同 - 由于需要额外的排序或聚合,它具有性能成本,并且它仅适用于序数数据类型。
答案 1 :(得分:12)
Craig的回答和评论中的结果查询共享相同的缺陷:表anwendung
位于LEFT JOIN
的右侧,这与您明显的意图相矛盾。您关心anwendung.name
并随意挑选autor.entwickler
。我会再回到那里了。
应该是:
SELECT DISTINCT ON (1) an.name, au.entwickler
FROM anwendung an
LEFT JOIN autor au ON an.name = au.anwendung;
DISTINCT ON (1)
只是DISTINCT ON (an.name)
的语法简写。这里允许进行位置参考。
如果某个应用(entwickler
)有多个开发人员(anwendung
),则会选择任意一个开发人员。如果你想要“第一个”(按字母顺序根据你的语言环境),你必须添加一个ORDER BY
子句:
SELECT DISTINCT ON (1) an.name, au.entwickler
FROM anwendung an
LEFT JOIN autor au ON an.name = au.anwendung
ORDER BY 1, 2;
正如@mdahlman暗示的那样,更典型的方式是:
SELECT an.name, min(au.entwickler) AS entwickler
FROM autor au
LEFT JOIN anwendung an ON an.name = au.anwendung
GROUP BY an.name;
或者,更好的是,清理您的数据模型,在anwendung
和autor
之间正确实施 n:m关系,将代理主键添加为{{1} }和anwendung
几乎不是唯一的,使用外键约束强制执行关系完整性并调整生成的查询:
autor
此查询检索每个应用程序的一行,其中一个关联的作者(按字母顺序排列第一个)或如果没有则检索NULL:
CREATE TABLE autor (
autor_id serial PRIMARY KEY -- surrogate primary key
, autor text NOT NULL);
INSERT INTO autor VALUES
(1, 'mike')
, (2, 'joe')
, (3, 'jane') -- worked on two apps
, (4, 'susi'); -- has no part in any apps (yet)
CREATE TABLE anwendung (
anwendung_id serial PRIMARY KEY -- surrogate primary key
, anwendung text UNIQUE); -- disallow duplicate names
INSERT INTO anwendung VALUES
(1, 'foo') -- has 3 authors linked to it
, (2, 'bar')
, (3, 'shark')
, (4, 'bait'); -- has no authors attached to it (yet).
CREATE TABLE autor_anwendung ( -- you might name this table "entwickler"
autor_id integer REFERENCES autor ON UPDATE CASCADE ON DELETE CASCADE
, anwendung_id integer REFERENCES anwendung ON UPDATE CASCADE ON DELETE CASCADE
, PRIMARY KEY (autor_id, anwendung_id)
);
INSERT INTO autor_anwendung VALUES
(1, 1)
,(2, 1)
,(3, 1)
,(2, 2)
,(3, 3);
结果:
SELECT DISTINCT ON (1) an.anwendung, au.autor
FROM anwendung an
LEFT JOIN autor_anwendung au_au USING (anwendung_id)
LEFT JOIN autor au USING (autor_id)
ORDER BY 1, 2;