SQL - 在列上排序数据而不将其包含在排名中

Posted

技术标签:

【中文标题】SQL - 在列上排序数据而不将其包含在排名中【英文标题】:SQL - Order Data on a Column without including it in ranking 【发布时间】:2021-07-02 07:21:15 【问题描述】:

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

这是桌子:

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

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

【问题讨论】:

我已经尝试过查询:“select id,prod_name,source_system,date,col1,col2,dense_rank() over(partition by prod_name,source_system order by col1,col2) as rnk from temp order按日期;"这也不起作用,因为 dense_rank 优先于数据排序。 不可读。将数据集显示为即用型代码格式的 CREATE TABLE + INSERT INTO 脚本。将您的查询格式化为代码。将所有输出格式化为表格。 mysql 或 RedShift,选择其中之一。 @Akina - 我希望它现在可读。任何指导都会有所帮助。 @RohanKapoor 。 . . PL/SQL 似乎与这个问题无关。该代码与 Oracle 不兼容。而甲骨文与 Redshift 关系不大。 【参考方案1】:
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

【讨论】:

感谢您的快速回复。我正在使用红移数据库。此外,这只是我创建的一个示例数据集。实际上,我需要在 25 列上决定是否应该增加排名。数据量也约为 9000 万。我也在考虑这些因素。那么对所有 25 列进行滞后然后比较它们是一个好主意吗?不使用CTE还有其他转机吗? @RohanKapoor 实际上我有 25 列我需要决定是否应该增加排名。数据量也约为 9000 万。我也将这些因素考虑在内。 此任务是否经常执行且对性能至关重要?如果是这样,那么考虑一些虚拟生成的列,它允许检测差异并在索引中使用它(或者如果 DBMS 允许,则通过表达式进行索引)。 这是我在此论坛上发布的第一个问题。为标记 my-sql 道歉。在您在第一条评论中指出它后,我将其删除。 此任务是否经常执行且对性能至关重要? - 是的,这经常被执行,问题是我在 Redshift 中经常遇到这个大数据集的磁盘满错误。 @RohanKapoor 我不使用 RedShift,只知道大概。快速文档搜索不显示生成的列或显式索引是可能的。所以我帮不了你,对不起。【参考方案2】:

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

基本思想是对每个产品/源系统的更改进行累积总和。在 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 的 dbfiddle。

请注意,日期的关系可能会导致问题 - 无论解决方案如何。这使用id 来打破僵局。也许id 可以在一般情况下使用,但是您的代码使用的是日期,所以它使用带有id 的日期。

【讨论】:

砰的一声@Gordon Linoff .. 感谢这个优化的解决方案。这给出了预期的结果。我刚刚按照您的建议使用了“id”。 @RohanKapoor 。 . .如果这回答了您的问题,您可以接受它作为答案。

以上是关于SQL - 在列上排序数据而不将其包含在排名中的主要内容,如果未能解决你的问题,请参考以下文章

使用 SQL Server Rank 函数对行进行排名而不跳过排名号

可以隐藏 SlickGrid 列而不将其从“列”数组中删除吗?

SQL查询结果加入排序值的问题

在特定列上排名时如何分区?

SQL Server排名函数与排名开窗函数

SQL Server排名函数与排名开窗函数