无法计算 CTE 子查询输出之间的差异以用于更大的 PostgreSQL 查询输出列
Posted
技术标签:
【中文标题】无法计算 CTE 子查询输出之间的差异以用于更大的 PostgreSQL 查询输出列【英文标题】:Unable to calculate difference between CTE subquery outputs for use in larger PostgreSQL query output column 【发布时间】:2016-10-29 12:24:43 【问题描述】:在 Shell 中使用 PostgreSQL v9.4.5 我通过运行create database moments
在psql
中创建了一个名为moments 的数据库。然后我创建了一个时刻表:
CREATE TABLE moments
(
id SERIAL4 PRIMARY KEY,
moment_type BIGINT NOT NULL,
flag BIGINT NOT NULL,
time TIMESTAMP NOT NULL,
UNIQUE(moment_type, time)
);
INSERT INTO moments (moment_type, flag, time) VALUES (1, 7, '2016-10-29 12:00:00');
INSERT INTO moments (moment_type, flag, time) VALUES (1, -30, '2016-10-29 13:00:00');
INSERT INTO moments (moment_type, flag, time) VALUES (3, 5, '2016-10-29 14:00:00');
INSERT INTO moments (moment_type, flag, time) VALUES (2, 9, '2016-10-29 18:00:00');
INSERT INTO moments (moment_type, flag, time) VALUES (2, -20, '2016-10-29 17:00:00');
INSERT INTO moments (moment_type, flag, time) VALUES (3, 10, '2016-10-29 16:00:00');
我运行select * from moments
来查看表格:
时刻表
id | moment_type | flag | time
----+-------------+------+---------------------
1 | 1 | 7 | 2016-10-29 12:00:00
2 | 1 | -30 | 2016-10-29 13:00:00
3 | 3 | 5 | 2016-10-29 14:00:00
4 | 2 | 9 | 2016-10-29 18:00:00
5 | 2 | -20 | 2016-10-29 17:00:00
6 | 3 | 10 | 2016-10-29 16:00:00
然后我尝试编写一个产生以下输出的 SQL 查询,由此对于每对重复的 moment_type 值,它返回具有最新时间戳值的 moment_type 的标志值与第二个的标志值之间的差异最近的时间戳值,并按 moment_type 升序列出结果。
预期的 SQL 查询输出
moment_type | flag |
------------+------+
1 | -37 | (i.e. -30 - 7)
2 | 29 | (i.e. 9 - -20)
3 | 5 | (i.e. 10 - 5)
我想出的SQL查询如下,它使用WITH查询写了多个Common Table Expressions (CET)子查询,作为最后更大的SELECT查询中的临时表。我还使用SQL function 来计算两个子查询输出之间的差异(或者我认为我可以只使用DIFFERENCE DIFFERENCE(most_recent_flag, second_most_recent_flag) AS flag
而不是函数):
CREATE FUNCTION difference(most_recent_flag, second_most_recent_flag) RETURNS numeric AS $$
SELECT $1 - $2;
$$ LANGUAGE SQL;
-- get two flags that have the most recent timestamps
WITH two_most_recent_flags AS (
SELECT moments.flag
FROM moments
ORDER BY moments.time DESC
LIMIT 2
),
-- get one flag that has the most recent timestamp
most_recent_flag AS (
SELECT *
FROM two_most_recent_flags
ORDER BY flag DESC
LIMIT 1
),
-- get one flag that has the second most recent timestamp
second_most_recent_flag AS (
SELECT *
FROM two_most_recent_flags
ORDER BY flag ASC
LIMIT 1
)
SELECT DISTINCT ON (moments.moment_type)
moments.moment_type,
difference(most_recent_flag, second_most_recent_flag) AS flag
FROM moments
ORDER BY moment_type ASC
LIMIT 2;
但是当我在 PostgreSQL 中运行上述 SQL 查询时,它返回以下错误:
ERROR: column "most_recent_flag" does not exist
LINE 21: difference(most_recent_flag, second_most_recent_flag) AS fla...
问题
我可以使用哪些技术以及如何应用它们来克服此错误,并计算和显示flag
列中的差异以实现预期的 SQL 查询输出?
注意:Window Function 可能会以某种方式使用,因为它跨表行执行计算
【问题讨论】:
【参考方案1】:使用lag()
窗口函数:
select moment_type, difference
from (
select *, flag- lag(flag) over w difference
from moments
window w as (partition by moment_type order by time)
) s
where difference is not null
order by moment_type
moment_type | difference
-------------+------------
1 | -37
2 | 29
3 | 5
(3 rows)
【讨论】:
【参考方案2】:一种方法是使用条件聚合。窗口函数row_number()
可用于识别第一个和最后一个时间值:
select m.moment_type,
(max(case when seqnum_desc = 1 then flag end) -
min(case when seqnum_asc = 1 then flag end)
)
from (select m.*,
row_number() over (partition by m.moment_type order by m.time) as seqnum_asc,
row_number() over (partition by m.moment_type order by m.time desc) as seqnum_desc
from moments m
) m
group by m.moment_type;
【讨论】:
以上是关于无法计算 CTE 子查询输出之间的差异以用于更大的 PostgreSQL 查询输出列的主要内容,如果未能解决你的问题,请参考以下文章