如何找到 Redshift SQL 中同一列中小于和等于值的 COUNT 直到下一个最高值?

问题描述 投票:0回答:1

这可能有点复杂,所以请耐心等待。我希望在当前看起来像这样的表中创建一个新列 free_duration,当按月份升序按国家/地区和产品分组时 -

month   country product         stock
7/1/10  Wakanda Vibranium   7166
8/1/10  Wakanda Vibranium   6189
9/1/10  Wakanda Vibranium   1987
10/1/10 Wakanda Vibranium   98
11/1/10 Wakanda Vibranium   23
12/1/10 Wakanda Vibranium   1
1/1/11  Wakanda Vibranium   29999
2/1/11  Wakanda Vibranium   2314
3/1/11  Wakanda Vibranium   19

对于给定行 x1,新列 free_duration 基本上是 stock 列所在的月数 值小于或等于该行中的库存列值 x1,直到出现更多库存(即库存值接下来增加)。

在示例中,对于第 1 行,按月份升序排列,7 月 '10 的库存为 7166 - 因此 7 月 '10 的 free_duration 值应为 5,因为连续 5 个月,行中的库存列值小于 7166 1,直到达到下一个更高的值。同样,对于 2010 年 12 月第 6 行,可用库存为 1,并且考虑到下一个月库存大于 1,free_duration 应为 0。

总而言之,我需要在一列中取一个值,对同一列中所有小于或等于的值进行倒数,直到达到下一个直接更大的值。

我的查询几乎做到了这一点,但有一个逻辑缺陷 - 在每个组中,它最终会计算所有较小的值,并且不会设置限制计数的上限,直到达到下一个更高的值。

WITH ranking AS (SELECT month,
                        country,
                        product,
                        stock,
                        ROW_NUMBER() OVER (PARTITION BY country, product ORDER BY month) AS rn
                 FROM data_table),
     free_duration_cte AS (SELECT r1.month,
                               r1.country,
                               r1.product,
                               r1.stock,
                               COALESCE((SELECT COUNT(*)
                                         FROM ranking r2
                                         WHERE r1.country = r2.country
                                           AND r1.product = r2.product
                                           AND r2.rn > r1.rn
                                           AND r2.stock <=
                                               r1.stock), 0) AS free_duration
                        FROM ranking r1)
SELECT *
FROM free_duration_cte
ORDER BY country, product, month;

我已经尝试了多种其他方法来仅设置上限,最简单的方法是使用 FIRST_VALUE(),这在 Redshift 中是不被接受的。任何有助于做出所需改变的帮助将不胜感激。

sql amazon-web-services amazon-redshift common-table-expression
1个回答
0
投票

这是您要找的吗?

create table table_1 (
  month   date,
  country varchar(16),
  product varchar(16),        
  stock int);

insert into table_1 values 
('2010-7-1', 'Wakanda', 'Vibranium', 7166),
('2010-8-1', 'Wakanda', 'Vibranium', 6189),
('2010-9-1', 'Wakanda', 'Vibranium', 1987),
('2010-10-1', 'Wakanda', 'Vibranium', 98),
('2010-11-1', 'Wakanda', 'Vibranium', 23),
('2010-12-1', 'Wakanda', 'Vibranium', 1),
('2011-1-1', 'Wakanda', 'Vibranium', 29999),
('2011-2-1', 'Wakanda', 'Vibranium', 2314),
('2011-3-1', 'Wakanda', 'Vibranium', 19);

with min_local as (
select *,
  nvl(min(stock) over(partition by country, product order by month rows between current row and unbounded following), stock) local_min
from table_1),
change as (
select *,
  decode(lag(local_min) over(partition by country, product order by month), local_min, 0, 1) change
from min_local),
groups as (
select *,
  sum(change) over(partition by country, product order by month rows unbounded preceding) grp
from change)
select month, country, product, stock,
  count(*) over(partition by country, product, grp order by month desc rows unbounded preceding) - 1 as free_duration
from groups
order by month;
© www.soinside.com 2019 - 2024. All rights reserved.