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

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 precedingunbounded 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)

Gor*_*off 7

问题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)


S-M*_*Man 5

一年后,我得到了解决方案:

拿这个声明:

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不需要第三次排序的窗口。在这一刻,它是正确的。