SQL - 对列中的数据进行排序但不将其包含在排名中

SQL - Order Data on a Column without including it in ranking

所以我有一个场景,我需要在列上排序数据而不将其包含在 dense_rank() 中。这是我的示例数据集:

这是table:

create table temp
(
id integer,
prod_name varchar(max),
source_system integer,
source_date date,
col1 integer,
col2 integer);

这是数据集:

insert into temp
(id,prod_name,source_system,source_date,col1,col2)
values
(1,'ABC',123,'01/01/2021',50,60),

(2,'ABC',123,'01/15/2021',50,60),

(3,'ABC',123,'01/30/2021',40,60),

(4,'ABC',123,'01/30/2021',40,70),

(5,'XYZ',456,'01/10/2021',80,30),

(6,'XYZ',456,'01/12/2021',75,30),

(7,'XYZ',456,'01/20/2021',75,30),

(8,'XYZ',456,'01/20/2021',99,30);

现在,我想对数据执行 dense_rank(),这样对于“prod_name 和 source_system”的组合,只有在以下情况下排名才会递增col1 或 col2 发生了变化,但数据仍应按 source_date 的升序排列。 这是预期的结果:

id prod_name source_system source_date col1 col2 Dense_Rank
1 ABC 123 01-01-21 50 60 1
2 ABC 123 15-01-21 50 60 1
3 ABC 123 30-01-21 40 60 2
4 ABC 123 30-01-21 40 70 3
5 XYZ 456 10-01-21 80 30 1
6 XYZ 456 12-01-21 75 30 2
7 XYZ 456 20-01-21 75 30 2
8 XYZ 456 20-01-21 99 30 3

正如您在上面所看到的,日期在变化,但预计只有在 col1 或 col2 发生任何变化时排名才会发生变化。

如果我使用这个查询

select id,prod_name,source_system,source_date,col1,col2,
dense_rank() over(partition by prod_name,source_system order by source_date,col1,col2) as rnk
from temp;

那么结果会是:

id prod_name source_system source_date col1 col2 rnk
1 ABC 123 01-01-21 50 60 1
2 ABC 123 15-01-21 50 60 2
3 ABC 123 30-01-21 40 60 3
4 ABC 123 30-01-21 40 70 4
5 XYZ 456 10-01-21 80 30 1
6 XYZ 456 12-01-21 75 30 2
7 XYZ 456 20-01-21 75 30 3
8 XYZ 456 20-01-21 99 30 4

而且,如果我从排序函数中排除 source_date,即

select id,prod_name,source_system,source_date,col1,col2,
dense_rank() over(partition by prod_name,source_system order by col1,col2) as rnk
from temp;

那么我的结果是:

id prod_name source_system source_date col1 col2 rnk
3 ABC 123 30-01-21 40 60 1
4 ABC 123 30-01-21 40 70 2
1 ABC 123 01-01-21 50 60 3
2 ABC 123 15-01-21 50 60 3
6 XYZ 456 12-01-21 75 30 1
7 XYZ 456 20-01-21 75 30 1
5 XYZ 456 10-01-21 80 30 2
8 XYZ 456 20-01-21 99 30 3

两个结果都不正确。我怎样才能得到预期的结果?任何指导都会有所帮助。

WITH cte AS (
SELECT *,
       LAG(col1) OVER (PARTITION BY prod_name, source_system ORDER BY source_date, id) lag1,
       LAG(col2) OVER (PARTITION BY prod_name, source_system ORDER BY source_date, id) lag2
FROM temp
)
SELECT *,
       SUM(CASE WHEN (col1, col2) = (lag1, lag2)
                THEN 0
                ELSE 1 
                END) OVER (PARTITION BY prod_name, source_system ORDER BY source_date, id) AS `Dense_Rank` 
FROM cte
ORDER BY id;

https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ac70104c7c5dfb49c75a8635c25716e6

比较多个列时,我喜欢查看排序列的先前值,而不是单个列。这使得添加越来越多的列变得更加简单。

基本思路是对每个prod/source系统的变化进行累加。在 Redshift 中,我会这样表述:

select t.*,
       sum(case when prev_date = prev_date_2 then 0 else 1 end) over (
            partition by  prod_name, source_system
            order by source_date
            rows between unbounded preceding and current row
           )
from (select t.*,
             lag(source_date) over (partition by prod_name, source_system order by source_date, id) as prev_date,
             lag(source_date) over (partition by prod_name, source_system, col1, col2 order by source_date, id) as prev_date_2
      from temp t
     ) t
order by id;

认为 我有适合 Redshift 的语法。 Here 是一个使用 Postgres 的 db<>fiddle。

请注意,日期上的联系可能会导致问题——无论解决方案如何。这使用 id 来打破平局。也许 id 可以只是一般使用,但是你的代码使用的是日期,所以这里使用带有 id 的日期。