假设我有两张桌子。其中一个以一对多关系引用另一个:
CREATE TABLE t (
id SERIAL PRIMARY KEY
, name text
);
INSERT INTO t (name) VALUES ('one'), ('two'), ('three');
CREATE TABLE y (
id SERIAL PRIMARY KEY
, tid INTEGER REFERENCES t
, amount INTEGER
);
INSERT INTO y (tid, amount) VALUES
(1, 1),
(1, 2),
(1, 3),
(2, 1),
(3, 1);
Run Code Online (Sandbox Code Playgroud)
我可以从这些表中查询数据,以获得与 和t上设置的特定条件匹配的行:ty
SELECT row_to_json(t.*) as "t_nogroup" FROM t
JOIN y ON t.id = y.tid
WHERE t.id = 1
AND y.amount > 1;
Run Code Online (Sandbox Code Playgroud)
这产生
t_nogroup
=========
{"id":1,"name":"one"}
{"id":1,"name":"one"}
Run Code Online (Sandbox Code Playgroud)
是的,它有效,但包含重复项。我可以使用以下方法过滤掉重复项GROUP BY:
SELECT row_to_json(t.*) as "t_group" FROM t
JOIN y ON t.id = y.tid
WHERE t.id = 1
AND y.amount > 1
GROUP BY t.id;
Run Code Online (Sandbox Code Playgroud)
制作:
t_group
=======
{"id":1,"name":"one"}
Run Code Online (Sandbox Code Playgroud)
伟大的!但是,当我尝试交换t和y为非物化VIEWs 时:
CREATE VIEW vt AS SELECT t.* FROM t;
CREATE VIEW vy AS SELECT y.* FROM y;
Run Code Online (Sandbox Code Playgroud)
VIEW并使用新的s运行相同的查询
SELECT row_to_json(vt.*) as "view_t_nogroup" FROM vt
JOIN vy ON vt.id = vy.tid
WHERE vt.id = 1
AND vy.amount > 1;
SELECT row_to_json(vt.*) as "view_t_group" FROM vt
JOIN vy ON vt.id = vy.tid
WHERE vt.id = 1
AND vy.amount > 1
GROUP BY vt.id;
Run Code Online (Sandbox Code Playgroud)
然后我得到第二个查询的错误:
ERROR: column "vt.*" must appear in the GROUP BY clause or be used in an aggregate function
Run Code Online (Sandbox Code Playgroud)
为什么会出现这种情况?使用表时*看到的任何逻辑都应该仍然适用于视图,对吗?idtvt
小提琴链接:http://sqlfiddle.com/#!17/26b0e /5
我正在运行 PostgreSQL 9.6。我也知道这是一个人为的示例,但是我在一个非常相似的一对多关系中遇到了这个错误,其中GROUP BY需要多个语句,因此仅仅使用DISTINCT不是一个选项,就像这个示例一样。
出现这个问题是因为 Postgres 知道该表t具有(id)主键 - 因此我们可以GROUP BY t.id在列表中使用该表中的更多列,SELECT而无需聚合它们 - 但它无法为 view 推断出相同的情况vt。
对于提供的示例,您可以将它们重写为GROUP BY根本不使用。由于您不需要选择列表中第二个连接表中的任何列,因此您可以将其转换JOIN为子EXISTS查询,即将连接转换为半连接。现在表和视图半连接都可以正常工作,不会出现错误:
SELECT row_to_json(t.*) as "t_group" FROM t
WHERE t.id = 1
AND EXISTS
( SELECT 1
FROM y
WHERE t.id = y.tid
AND y.amount > 1
) ;
SELECT row_to_json(vt.*) as "t_group" FROM vt
WHERE vt.id = 1
AND EXISTS
( SELECT 1
FROM vy
WHERE vt.id = vy.tid
AND vy.amount > 1
) ;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6114 次 |
| 最近记录: |