获取每个分区第一条记录的最佳方法:FIRST_VALUE 与 ROW_NUMBER

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

我正在寻找使用 SQL 获取每个分区 (a,b) 的第一条记录(a,b,c 列)的最快方法。表大约有 10, 000, 000 行。

方法#1:

SELECT * FROM (
    SELECT a,b,c, 
    ROW_NUMBER() OVER ( PARTITION by a, b ORDER BY date DESC) as row_num
    FROM T 
) WHERE row_num =1

但它可能在幕后做了额外的工作 - 我只需要每个分区的第一行。

使用 FIRST_VALUE() 的方法 #2。由于 FIRST_VALUE() 返回表达式 让使用一些分隔符将 a、b、c 打包/连接成单个表达式,例如:

SELECT FIRST_VALUE(a+','+'b'+','+c) 
OVER ( PARTITION by a, b ORDER BY date  DESC rows unbounded preceding) FROM T

但在这种情况下,我需要解压结果,这是额外的步骤。

使用 FIRST_VALUE() 的方法 #3 - 对 a 、 b 重复 OVER (...) :

SELECT 
FIRST_VALUE(a) 
OVER ( PARTITION by a, b ORDER BY date  DESC rows unbounded preceding),
FIRST_VALUE(b) 
OVER ( PARTITION by a, b ORDER BY date  DESC rows unbounded preceding),
c 
FROM T

在方法 #3 中,我不知道数据库引擎(Redshift)是否足够智能,只能分区一次

sql amazon-redshift
2个回答
2
投票

第一个查询与其他两个不同。第一个每组仅返回一行。另外两个返回与原始查询中相同的行。

您应该使用能够实现您想要的功能的版本,我认为这是第一个版本。如果您将

select distinct
group by
添加到其他查询,这可能会增加开销,从而使它们变慢 - 但您可以测试您的数据,看看这是否属实。

您的直觉是正确的,第一个查询做了不必要的工作。在完全支持索引的数据库中,相关子查询通常更快。然而,我认为 Redshift 不会出现这种情况。


0
投票

SnowFlake 中可重复的基准测试: 方法 1 在 Snowflake 中效果更快。当我们增加第一个值的列数时,它会变得更快。 当值的数量增加时,方法#1 可以说更具可读性。

with gen as (
select 
    row_number() over(order by null) as row_number,
    trunc(row_number / 10) as group_number, 
    to_char(row_number) as string_value,
    '2' || to_char(row_number) as string_value_2
from table(GENERATOR( ROWCOUNT =>  100 * 1000 * 1000))
) 
, approach_1 as 
(
select
  row_number
, group_number  
, string_value as string_first_value
, string_value_2 as string_first_value_2
from   
  gen  
qualify 
  1 = row_number() over(partition by group_number order by row_number desc) 
) 
, approach_2 as 
(
select
  row_number
, group_number  
, first_value(string_value) over(partition by group_number order by row_number desc) as string_first_value
, first_value(string_value_2) over(partition by group_number order by row_number desc) as string_first_value_2
from   
  gen  
) 
-- select max(string_first_value_2 || string_first_value) from approach_1
-- approach_1 - 7.9 sec, 7.8 sec
-- 2 values: 8 sec
select max(string_first_value_2 || string_first_value) from approach_2
-- approach_2 - 1 value 8.7 sec, 9 sec
-- 2 values: 13 sec
© www.soinside.com 2019 - 2024. All rights reserved.