Redshift:获取行的排名,按条件过滤

Posted

技术标签:

【中文标题】Redshift:获取行的排名,按条件过滤【英文标题】:Redshift: Getting rank of a row, filtered by a condition 【发布时间】:2014-09-09 13:34:18 【问题描述】:

每次我向表中添加一行时,我都想知道与该点之前的表相比它的排名。这可以通过 RANK() 窗口函数轻松完成。但是,我正在努力寻找一种方法来发现它与表格相比的排名,直到那个点按值过滤

作为一个例子,我想最终得到这个高度做作的表格:

    date    |   name  | animal_bought | num_sloths_bought_before | num_camels_bought_before
------------+---------+---------------+--------------------------+--------------------------
 2014-09-01 | Vincent | sloth         | 0                        | 0
 2014-09-01 | Luis    | camel         | 0                        | 0
 2014-09-02 | Vincent | sloth         | 1                        | 0
 2014-09-02 | Luis    | camel         | 0                        | 1
 2014-09-02 | Kevin   | sloth         | 0                        | 0
 2014-09-03 | Vincent | camel         | 1                        | 0
 2014-09-04 | Deo     | camel         | 0                        | 0
 2014-09-04 | Vincent | sloth         | 2                        | 1
 2014-09-05 | Luis    | camel         | 0                        | 2
 2014-09-05 | Andrew  | sloth         | 0                        | 0

我最初想看看是否可以对窗口函数应用过滤器(例如RANK() OVER(PARTITION BY name WHERE animal_bought = 'sloth' ORDER BY date ASC) AS num_sloths_bought_before),但这在语法上不正确。然后我尝试添加一个子查询,如下:

SELECT
  date,
  name,
  animal_bought,
  ( SELECT
      RANK() OVER(PARTITION BY name ORDER BY date ASC) - 1
    FROM this_table
    WHERE animal_bought = 'sloth'
  ) AS num_sloths_bought_before
FROM source_table

但是 Redshift 抛出了这个错误:

ERROR:  This type of correlated subquery pattern is not supported yet

我还尝试将窗口函数放在 case 语句中(引发相同的错误)并在连接查询中计算排名(无法使其工作)。

【问题讨论】:

【参考方案1】:

嗯。我不认为这个查询会做你想做的事:

SELECT date, name, animal_bought,
       (SELECT RANK() OVER(PARTITION BY name ORDER BY date ASC) - 1
        FROM this_table
        WHERE animal_bought = 'sloth'
       ) AS num_sloths_bought_before
FROM source_table

有几个原因:

rank() 的使用表明this_table 中有不止一行匹配animal_bought。否则,您可以使用聚合函数。 如果只有一行与where 子句匹配,则该值始终为1,因为where 子句在rank() 之前处理。 您的问题只提到一个表,但您的查询有两个

也许您只想要没有子查询的rank()

SELECT date, name, animal_bought,
       RANK() OVER (PARTITION BY name, animal ORDER BY date ASC) - 1 as NumberBoughtBefore
FROM source_table;

如果你想对两种动物都使用它,那么不要使用rank(),使用累积和:

SELECT date, name, animal_bought,
       sum(case when animal = 'sloth' then 1 else 0 end) over (partition by name order by date) as SlothsBefore,
       sum(case when animal = 'camel' then 1 else 0 end) over (partition by name order by date) as CamelsBefore
FROM source_table;

编辑:

SELECT date, name, animal_bought,
       (sum(case when animal = 'sloth' then 1 else 0 end) over (partition by name order by date) -
        (case when animal = 'sloth' then 1 else 0 end)
       ) as SlothsBefore,
       (sum(case when animal = 'camel' then 1 else 0 end) over (partition by name order by date) -
        (case when animal = 'camel' then 1 else 0 end)
       ) as CamelsBefore
FROM source_table;

【讨论】:

谢谢,戈登。不幸的是,这会生成一个表格,其中包含一个名字在每一行上购买动物的总次数,而不是它在该行之前发生的总次数。即:Vincent 的每一行在sloths_bought 中都有一个 3,在camel_bought 中有一个 1,而不是逐行增加。 @Serenthia 。 . .只需减去 1 或在分区中使用 range/rows 子句。 恐怕这会产生相同的结果 - SUM() 会计算事件发生的所有时间,然后将相同的总数放在该人的每一行中。我需要在每一行上使用不同的数字(由RANK() 函数产生)。再次感谢您的意见! @Serenthia 。 . . sum() . . . over产生累积和吗? order by 应该是累积的。 对不起 - 我不得不把它放下一段时间。返回时,我发现您的解决方案确实有效。它只是缺少框架子句ROWS UNBOUNDED PRECEDING。它以前不起作用的原因是因为我的框架条款不正确。非常感谢您的意见!

以上是关于Redshift:获取行的排名,按条件过滤的主要内容,如果未能解决你的问题,请参考以下文章

按 id 和某些条件过滤掉数据分组

针对不同过滤条件的开闭原理

基于Python中的多个条件进行过滤

Laravel - 按“字段及其关系模型字段”之间的条件过滤模型

Amazon Redshift - 在 where 条件下的变量

Django按条件过滤