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 的日期。
所以我有一个场景,我需要在列上排序数据而不将其包含在 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 的日期。