窗口函数:last_value(ORDER BY ... ASC)与last_value相同(ORDER BY ... DESC)

时间:2017-02-17 13:24:11

标签: 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)
;

当前查询

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 precedingunbounded 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)相同?

3 个答案:

答案 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