S-M*_*Man 7 sql postgresql window-functions
样本数据
CREATE TABLE test
(id integer, session_ID integer, value integer)
;
INSERT INTO test
(id, session_ID, value)
VALUES
(0, 2, 100),
(1, 2, 120),
(2, 2, 140),
(3, 1, 900),
(4, 1, 800),
(5, 1, 500)
;
Run Code Online (Sandbox Code Playgroud)
当前查询
select
id,
last_value(value) over (partition by session_ID order by id) as last_value_window,
last_value(value) over (partition by session_ID order by id desc) as last_value_window_desc
from test
ORDER BY id
Run Code Online (Sandbox Code Playgroud)
我在使用last_value()窗口函数时遇到问题:http :
//sqlfiddle.com/#!15/bcec0/2
在小提琴中,我尝试使用last_value()查询中的排序方向。
编辑:
问题不在于:为什么我没有得到所有时间的最后一个值,以及如何使用frame子句(unbounded preceding和unbounded following)。我知道的差异first_value(desc),并last_value()与该问题last_value()不给你的所有时间最后一个值:
默认frame子句在当前行之前是无界的。因此,第一个值始终是带子句的第一行。因此,仅存在一行(frame子句仅包括这一行)还是仅包含一行(frame子句包括全部一百行)都没有关系。结果始终是第一个。在DESC顺序中是相同的:DESC更改排序顺序,然后第一行是最后一个值,无论您获得多少行。
与last_value()行为非常相似:如果有一行,它将为您提供默认frame子句的最后一个值:这一行。在第二行,frame子句包含两行,最后一行是第二行。这就是为什么last_value()没有给您所有行的最后一行,而是只有最后一行直到当前行的原因。
但是,如果我将顺序更改为DESC,则我希望所有的第一行都是最后一行,因此我在第一行中得到了这一行,而不是在第二行中得到了最后一行,依此类推。但这不是结果。为什么?
对于当前的例子这些都是结果first_value(),first_value(desc),last_value(),last_value(desc)和我所期待的last_value(desc):
id | fv_asc | fv_desc | lv_asc | lv_desc | lv_desc(expecting)
----+--------+---------+--------+---------+--------------------
0 | 100 | 140 | 100 | 100 | 140
1 | 100 | 140 | 120 | 120 | 120
2 | 100 | 140 | 140 | 140 | 100
3 | 900 | 500 | 900 | 900 | 500
4 | 900 | 500 | 800 | 800 | 800
5 | 900 | 500 | 500 | 500 | 900
Run Code Online (Sandbox Code Playgroud)
对我来说,ORDER BY DESC默认框架子句last_value()调用中似乎忽略了该标志。但这不在first_value()通话范围之内。所以我的问题是:为什么last_value()结果与相同last_value(desc)?
问题LAST_VALUE()在于,窗口条款的默认规则会删除您真正想要的值。这是一个非常微妙的问题,在所有支持此功能的数据库中都是如此。
这来自Oracle博客:
当我们讨论windowing子句的主题时,FIRST和LAST函数的隐式和不可更改的window子句是未绑定的前缀和未绑定的跟随行,换句话说,分区中的所有行。对于FIRST_VALUE和LAST_VALUE,默认但可更改的窗口条款是ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,换句话说,我们排除了当前行之后的行。当我们在寻找列表的第一行(FIRST_VALUE)时,从列表底部删除行没有什么不同,但是当我们寻找列表的最后一行(LAST_VALUE)时确实有所不同,因此您通常需要在使用LAST_VALUE时明确指定“在未绑定的前导和未绑定的跟随之间的行”,或者仅使用FIRST_VALUE并反转排序顺序。
因此,只需使用即可FIRST_VALUE()。这就是您想要的:
with test (id, session_ID, value) as (
(VALUES (0, 2, 100),
(1, 2, 120),
(2, 2, 140),
(3, 1, 900),
(4, 1, 800),
(5, 1, 500)
)
)
select id,
first_value(value) over (partition by session_ID order by id) as first_value_window,
first_value(value) over (partition by session_ID order by id desc) as first_value_window_desc
from test
order by id
Run Code Online (Sandbox Code Playgroud)
一年后,我得到了解决方案:
拿这个声明:
SELECT
id,
array_accum(value) over (partition BY session_ID ORDER BY id) AS window_asc,
first_value(value) over (partition BY session_ID ORDER BY id) AS first_value_window_asc,
last_value(value) over (partition BY session_ID ORDER BY id) AS last_value_window_asc,
array_accum(value) over (partition BY session_ID ORDER BY id DESC) AS window_desc,
first_value(value) over (partition BY session_ID ORDER BY id DESC) AS first_value_window_desc,
last_value(value) over (partition BY session_ID ORDER BY id DESC) AS last_value_window_desc
FROM
test
ORDER BY
id
Run Code Online (Sandbox Code Playgroud)
这给
id window_asc first_value_window_asc last_value_window_asc window_desc first_value_window_desc last_value_window_desc
-- ------------- ---------------------- --------------------- ------------- ----------------------- ----------------------
0 {100} 100 100 {140,120,100} 140 100
1 {100,120} 100 120 {140,120} 140 120
2 {100,120,140} 100 140 {140} 140 140
3 {900} 900 900 {500,800,900} 500 900
4 {900,800} 900 800 {500,800} 500 800
5 {900,800,500} 900 500 {500} 500 500
Run Code Online (Sandbox Code Playgroud)
在array_accum给出了使用的窗口。在那里您可以看到窗口的第一个值和当前的最后一个值。
发生的事情显示了执行计划:
"Sort (cost=444.23..449.08 rows=1940 width=12)"
" Sort Key: id"
" -> WindowAgg (cost=289.78..338.28 rows=1940 width=12)"
" -> Sort (cost=289.78..294.63 rows=1940 width=12)"
" Sort Key: session_id, id"
" -> WindowAgg (cost=135.34..183.84 rows=1940 width=12)"
" -> Sort (cost=135.34..140.19 rows=1940 width=12)"
" Sort Key: session_id, id"
" -> Seq Scan on test (cost=0.00..29.40 rows=1940 width=12)"
Run Code Online (Sandbox Code Playgroud)
在那里你可以看到: 首先是ORDER BY id前三个窗口函数。
这给出(如问题所述)
id window_asc first_value_window_asc last_value_window_asc
-- ------------- ---------------------- ---------------------
3 {900} 900 900
4 {900,800} 900 800
5 {900,800,500} 900 500
0 {100} 100 100
1 {100,120} 100 120
2 {100,120,140} 100 140
Run Code Online (Sandbox Code Playgroud)
然后你可以看到另一种排序:ORDER BY id DESC对于接下来的三个窗口函数。这种排序给出:
id window_asc first_value_window_asc last_value_window_asc
-- ------------- ---------------------- ---------------------
5 {900,800,500} 900 500
4 {900,800} 900 800
3 {900} 900 900
2 {100,120,140} 100 140
1 {100,120} 100 120
0 {100} 100 100
Run Code Online (Sandbox Code Playgroud)
通过这种排序,DESC窗口函数被执行。该array_accum列显示生成的窗口:
id window_desc
-- -------------
5 {500}
4 {500,800}
3 {500,800,900}
2 {140}
1 {140,120}
0 {140,120,100}
Run Code Online (Sandbox Code Playgroud)
结果 ( first_value DESCand)last_value DESC现在与 完全相同last_value ASC:
id window_asc last_value_window_asc window_desc last_value_window_desc
-- ------------- --------------------- ------------- ----------------------
5 {900,800,500} 500 {500} 500
4 {900,800} 800 {500,800} 800
3 {900} 900 {500,800,900} 900
2 {100,120,140} 140 {140} 140
1 {100,120} 120 {140,120} 120
0 {100} 100 {140,120,100} 100
Run Code Online (Sandbox Code Playgroud)
现在我很清楚为什么last_value ASC等于last_value DESC. 这是因为第二个ORDER窗口函数给出了一个倒置的窗口。
(执行计划的最后ORDER BY一种是语句的最后一种。)
作为一个小奖励:此查询显示了一点优化潜力:如果您DESC首先调用窗口,然后调用ASC不需要第三次排序的窗口。在这一刻,它是正确的。