我有一个问题,我需要在PostgreSQL中的各个表中获取最后一项。
以下代码有效,并返回最新更新的类型以及上次更新的时间。
问题是,这个查询需要用作子查询,所以我想从这个查询中选择类型和最后更新的值,而PostgreSQL似乎不喜欢这个...(Subquery must return only one column
)
有什么建议吗?
SELECT last.type, last.max FROM (
SELECT MAX(a.updated_at), 'a' AS type FROM table_a a WHERE a.ref = 5 UNION
SELECT MAX(b.updated_at), 'b' AS type FROM table_b b WHERE b.ref = 5
) AS last ORDER BY max LIMIT 1
在CTE内部使用查询;
WITH sql_query as (
SELECT id, name, address, (...other columns),
last.type, last.max FROM (
SELECT MAX(a.updated_at), 'a' AS type FROM table_a a WHERE a.ref = 5 UNION
SELECT MAX(b.updated_at), 'b' AS type FROM table_b b WHERE b.ref = 5
) AS last ORDER BY max LIMIT 1
FROM table_c
WHERE table_c.fk_id = 1
)
答案 0 :(得分:1)
固有的问题是SQL(所有SQL不仅仅是Postgres)要求select子句中使用的子查询只能返回单个值。如果你考虑一下这个限制,它确实有意义。 select子句返回行和一定数量的列,每行。列位置是网格中的单个位置。您可以通过将连接放入单个位置(或单个“复杂类型”,如JSON值)来稍微弯曲该规则,但无论如何,它仍然是该网格中的单个位置。
但是,您确实需要2个单独的列,并且需要从同一行返回两个列,因此我建议使用LIMIT 1
来代替ROW_NUMBER()
,以便实现此目的:
WITH LastVals as (
SELECT type
, max_date
, row_number() over(order by max_date DESC) as rn
FROM (
SELECT MAX(a.updated_at) AS max_date, 'a' AS type FROM table_a a WHERE a.ref = 5
UNION ALL
SELECT MAX(b.updated_at) AS max_date, 'b' AS type FROM table_b b WHERE b.ref = 5
)
)
, sql_query as (
SELECT id
, name, address, (...other columns)
, (select type from lastVals where rn = 1) as last_type
, (select max_date from lastVals where rn = 1) as last_date
FROM table_c
WHERE table_c.fk_id = 1
)
顺便提一下,在你的子查询中你应该使用UNION ALL
类型为常数,如“a”或“b”,那么即使MAX(a.updated_at)
对于2个或更多个表是相同的,行仍然会由于类型的不同而具有独特性。 UNION
会尝试删除重复的行,但此处它不会有所帮助,因此请使用UNION ALL
避免浪费精力。
另一种方法是使用LEFT JOIN
而不是
SELECT id
, name, address, (...other columns)
, lastVals.type
, LastVals.last_date
FROM table_c
WHERE table_c.fk_id = 1
LEFT JOIN (
SELECT type
, last_date
, row_number() over(order by last_date DESC) as rn
FROM (
SELECT MAX(a.updated_at) AS last_date, 'a' AS type FROM table_a a WHERE a.ref = 5
UNION ALL
SELECT MAX(b.updated_at) AS last_date, 'b' AS type FROM table_b b WHERE b.ref = 5
)
) LastVals ON LastVals.rn = 1