如何使用where子句执行窗口函数?

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

从下表的第 1-4 列中,我想创建一个可以返回第 5 列和第 6 列的查询:

我的日期 cat_1_id cat_2_id 我的值 cat_2_id_1_值 cat_2_id_2_值
2024年1月1日 1 1 1 1
2024年1月1日 2 1 2 2
2024年2月1日 1 2 3 1 3
2024年2月1日 2 2 4 2 4
2024年3月1日 1 1 5 5 3
2024年3月1日 2 1 6 6 4

查询逻辑:

这本质上是第 1-4 列中包含的数据的旋转。

第 5 栏:

  • 如果
    cat_2_id
    = 1,则显示该行的
    my_value
  • 如果
    cat_2_id
    != 1,则在最后一行显示
    my_value
    ,其中
    cat_id_1
    与当前行匹配且
    cat_2_id
    = 1

第 6 栏:

这与第 5 列的原理相同,只是

cat_2_id
= 2。所以:

  • 如果
    cat_2_id
    = 2,则显示该行的
    my_value
  • 如果
    cat_2_id
    != 2,则在最后一行显示
    my_value
    ,其中
    cat_id_1
    与当前行匹配且
    cat_2_id
    = 2

目前进度:

我已经找到了一个涉及

CASE
的解决方案:

    如果满足
  • my_value
    条件,则
    得到
    cat_2_id
    ,或者;
  • 使用
    my_value
    窗口函数以及
    LAST_VALUE
    (
    PARTITION BY
    ) 和
    cat_1_id
    (
    ORDER BY
    ) 和
    my_date
    的组合获得最后一行的
    RANGE BETWEEN

但是,我无法理解如何在窗口函数中构建附加过滤器以确保最后一行满足

cat_2_id
条件。

我发现this post使用

FILTER
用于postgresql,但我在MySQL文档中找不到这个(而且它说它没有针对非聚合窗口函数实现)。

如果没有窗口函数方法可以做到这一点,那么我会对任何能完成这项工作的东西感到满意!

这是要重新创建的数据:

CREATE TABLE test.pivot_cols (
    pivot_by_cols_id INT AUTO_INCREMENT PRIMARY KEY,
    my_date DATE NOT NULL, 
    cat_1_id INT NOT NULL, 
    cat_2_id INT NOT NULL, 
    my_value INT NOT NULL
);
INSERT INTO `test`.`pivot_cols` (`my_date`, `cat_1_id`, `cat_2_id`, `my_value`) VALUES ('2000-01-01', '1', '1', '1');
INSERT INTO `test`.`pivot_cols` (`my_date`, `cat_1_id`, `cat_2_id`, `my_value`) VALUES ('2000-01-01', '2', '1', '2');
INSERT INTO `test`.`pivot_cols` (`my_date`, `cat_1_id`, `cat_2_id`, `my_value`) VALUES ('2000-01-02', '1', '2', '3');
INSERT INTO `test`.`pivot_cols` (`my_date`, `cat_1_id`, `cat_2_id`, `my_value`) VALUES ('2000-01-02', '2', '2', '4');
INSERT INTO `test`.`pivot_cols` (`my_date`, `cat_1_id`, `cat_2_id`, `my_value`) VALUES ('2000-01-02', '1', '1', '5');
INSERT INTO `test`.`pivot_cols` (`my_date`, `cat_1_id`, `cat_2_id`, `my_value`) VALUES ('2000-01-02', '2', '1', '6');
mysql window-functions mysql-8.0
1个回答
0
投票

您可以从更容易实现和理解的相关子查询开始:

select *, case when cat_2_id = 1 then my_value else (
  select my_value
  from pivot_cols as x
  where cat_1_id = pivot_cols.cat_1_id
  and cat_2_id = 1
  and my_date < pivot_cols.my_date
  order by my_date desc
  limit 1
) end as id_1_value, case when cat_2_id = 2 then my_value else (
  select my_value
  from pivot_cols as x
  where cat_1_id = pivot_cols.cat_1_id
  and cat_2_id = 2
  and my_date < pivot_cols.my_date
  order by my_date desc
  limit 1
) end as id_2_value
from pivot_cols
order by my_date, cat_1_id

如果您必须使用窗口函数,如果 MySQL 在窗口函数中支持

ignore nulls
,那么事情可能会很简单。遗憾的是事实并非如此,因此您需要使用一些技巧。在以下查询中,我使用
max() over ()
有条件地查找先前日期。使用附加连接来获取值:

with cte as (
  select
    *,
    max(case when cat_2_id = 1 then my_date end) over prev_rows as date_1,
    max(case when cat_2_id = 2 then my_date end) over prev_rows as date_2
  from pivot_cols
  window prev_rows as (
    partition by cat_1_id
    order by my_date rows between unbounded preceding and 1 preceding
  )
)
select
  cte.*,
  case when cte.cat_2_id = 1 then cte.my_value else j1.my_value end as id_1_value,
  case when cte.cat_2_id = 2 then cte.my_value else j2.my_value end as id_2_value
from cte
left join pivot_cols as j1 on cte.cat_1_id = j1.cat_1_id and cte.date_1 = j1.my_date 
left join pivot_cols as j2 on cte.cat_1_id = j2.cat_1_id and cte.date_2 = j2.my_date
order by cte.my_date, cte.cat_1_id

DB<>Fiddle 上的演示

© www.soinside.com 2019 - 2024. All rights reserved.