我正在寻找使用 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)是否足够智能,只能分区一次
第一个查询与其他两个不同。第一个每组仅返回一行。另外两个返回与原始查询中相同的行。
您应该使用能够实现您想要的功能的版本,我认为这是第一个版本。如果您将
select distinct
或 group by
添加到其他查询,这可能会增加开销,从而使它们变慢 - 但您可以测试您的数据,看看这是否属实。
您的直觉是正确的,第一个查询做了不必要的工作。在完全支持索引的数据库中,相关子查询通常更快。然而,我认为 Redshift 不会出现这种情况。
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