示例数据
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)
;
当前查询
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
我遇到了last_value()
窗口函数的问题:
http://sqlfiddle.com/#!15/bcec0/2
在小提琴中,我试图使用last_value()
查询中的排序方向。
修改
问题不是:为什么我没有得到所有时间的最后一个值以及如何使用frame子句(unbounded preceding
和unbounded following
)。我知道first_value(desc)
和last_value()
的区别以及last_value()
没有给你最后一次值的问题:
默认的frame子句在当前行之前是无界的。所以第一个值总是给第一行带有子句。因此,如果只有一行(框架子句仅包括这一行)或者一个被占用的(框架子句包括全部),则无关紧要。结果始终是第一个。在DESC顺序中,它是相同的:DESC更改排序顺序,然后第一行是最后一个值,无论您获得多少行。
使用last_value()
时,行为非常相似:如果您有一行,它会为您提供默认帧子句的最后一个值:这一行。在第二行,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
对我来说,似乎在默认的帧子句ORDER BY DESC
调用中忽略了last_value()
标志。但它不在first_value()
电话中。所以我的问题是:为什么last_value()
结果与last_value(desc)
相同?
答案 0 :(得分:5)
LAST_VALUE()
的问题在于窗口子句的默认规则会删除您真正想要的值。这是一个非常微妙的问题,并且在支持此功能的所有数据库中都是如此。
This来自Oracle博客:
虽然我们讨论的是关于窗口条款的主题,但是隐含的和 第一个和最后一个函数的不可更改的窗口子句是ROWS 换句话说,在无限制的前进和无限的后续之间 我们分区中的所有行。对于FIRST_VALUE和LAST_VALUE,默认值 但是可变的窗口条款是在无限制的前进之间的行程 和当前行,换句话说,我们排除当前行之后的行。 从列表底部删除行没有任何区别 寻找列表中的第一行(FIRST_VALUE),但确实有一个 我们在寻找列表中的最后一行时的区别 (LAST_VALUE)因此您通常需要指定ROWS BETWEEN 使用时明确无限制地进行预定和无约束 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
答案 1 :(得分:2)
一年后我得到了解决方案:
采取这个声明:
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
这给出了
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
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)"
在那里你可以看到:首先,前三个窗口函数有一个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
然后你可以看到另一种类型: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
通过这种排序,执行DESC
窗口函数。 array_accum
列显示生成的窗口:
id window_desc
-- -------------
5 {500}
4 {500,800}
3 {500,800,900}
2 {140}
1 {140,120}
0 {140,120,100}
结果(first_value DESC
和)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
现在我很清楚为什么last_value ASC
等于last_value DESC
。这是因为窗口的第二个ORDER
函数给出了倒置窗口。
(最后一种执行计划是语句的最后一个ORDER BY
。)
作为一点奖励:此查询显示了一点优化潜力:如果先调用DESC
窗口,然后调用ASC
窗口,则不需要第三种类型。这一刻它是正确的。
答案 2 :(得分:0)
Check how the window frame is defined。这个例子可能会有所帮助:
select
id,
last_value(value) over (
partition by session_id
order by id
) as lv_asc,
last_value(value) over (
partition by session_id
order by id desc
) as lv_desc,
last_value(value) over (
partition by session_id
order by id
rows between unbounded preceding and unbounded following
) as lv_asc_unbounded,
last_value(value) over (
partition by session_id
order by id desc
rows between unbounded preceding and unbounded following
) as lv_desc_unbounded
from t
order by id;
id | lv_asc | lv_desc | lv_asc_unbounded | lv_desc_unbounded
----+--------+---------+------------------+-------------------
0 | 100 | 100 | 140 | 100
1 | 120 | 120 | 140 | 100
2 | 140 | 140 | 140 | 100
3 | 900 | 900 | 500 | 900
4 | 800 | 800 | 500 | 900
5 | 500 | 500 | 500 | 900