简介
我当前正在运行一个查询,据 DataEng 团队报告,该查询非常消耗资源。 我需要一些帮助来改进它/降低其成本。
案例
我有一个表格 (
status_history_table
),其中显示了我所有客户的状态以及该状态有效的日期范围。我需要一个查询,每天返回有多少客户已更改其状态以及有多少客户未更改状态。
只需要最近 365 天的数据。
status_history_table
示例:
用户ID | 状态 | 有效来源 | 有效 |
---|---|---|---|
X | '活跃' | 20240401 | 20240405 |
X | “不活跃” | 20240406 | 99991231 |
当我们将 valid_to 设置为“99991231”时,这意味着这是客户的当前状态,即,它一直有效,直到发生某些事情/改变它为止。
dim_date_table
是包含所有日期的维度表;
id_日期 | 日期_ |
---|---|
20240401 | 2024-04-01 |
20240402 | 2024-04-02 |
这是我当前使用的查询:
with
daily_status as (
select
h.user_id
, d.id_date
, h.status
from
status_history_table h
inner join dim_date_table d
on d.id_date between h.valid_from and h.valid_to
and d.date_ <= current_date
and d.date_ > current_date - 365
)
, lag as (
select
*
, lag(status,1)over(partition by user_id order by id_date asc) as previous_status
from
daily_status
)
select
id_date
case
when previous_status = status_braze then 'No change'
else 'Changed'
end as class,
count(1) as user_qty
from
lag
group by
1,2
我为此报告提取了更多信息,但此查询代表了正在使用的最重要的逻辑。 任何帮助将不胜感激。
更新1:
这是我正在使用的真实查询:
with
base as (
select
d.date_,
h.subscription_status as status,
braze_id
from
curated_data.braze_subscription_history h
inner join business_layer.dim_date d
on d.pk_date between h.valid_from and h.valid_to
and d.date_ <= current_date
and d.date_ > current_date - 365
where
h.source = 'PUSH'
and h.country_code = 'BR'
),
-- ve qual foi o status anterior de cada cliente
lag as (
select
*,
lag(status,1)over(partition by braze_id order by date_ asc) as previous_status
from
base
)
select
date_
, case
when previous_status = status then 'No change'
else 'Changed'
end as class,
count(1) as user_qty
from
lag
group by
1,2
运行“解释”的结果
XN HashAggregate (cost=1000679035081.81..1000679075081.81 rows=8000000 width=78)
-> XN Subquery Scan lag (cost=1000627708182.17..1000668036460.46 rows=1466482847 width=78)
-> XN Window (cost=1000627708182.17..1000649705424.87 rows=1466482847 width=45)
Partition: h.braze_id
Order: d.date_
-> XN Sort (cost=1000627708182.17..1000631374389.29 rows=1466482847 width=45)
Sort Key: h.braze_id, d.date_
-> XN Network (cost=7525.05..404438272.74 rows=1466482847 width=45)
Distribute
-> XN Nested Loop DS_BCAST_INNER (cost=7525.05..404438272.74 rows=1466482847 width=45)
Join Filter: (("inner".pk_date <= ("outer".valid_to)::bigint) AND ("inner".pk_date >= ("outer".valid_from)::bigint))
-> XN Seq Scan on braze_subscription_history h (cost=0.00..1472107.32 rows=36159851 width=49)
Filter: (((source)::text = 'PUSH'::text) AND ((country_code)::text = 'BR'::text))
-> XN Materialize (cost=7525.05..7528.70 rows=365 width=12)
-> XN Seq Scan on dim_date d (cost=0.00..224.69 rows=365 width=12)
Filter: ((date_ > '2023-04-09'::date) AND (date_ <= '2024-04-08'::date))
----- Nested Loop Join in the query plan - review the join predicates to avoid Cartesian products -----
DDLS:
CREATE TABLE IF NOT EXISTS curated_data.braze_subscription_history
(
country_code VARCHAR(2) ENCODE zstd
,braze_id VARCHAR(32) ENCODE zstd
,external_id VARCHAR(32) ENCODE zstd
,source VARCHAR(16) ENCODE zstd
,subscription_status VARCHAR(16) ENCODE zstd
,valid_from INTEGER ENCODE az64
,valid_to INTEGER ENCODE az64
)
DISTSTYLE EVEN
CREATE TABLE IF NOT EXISTS business_layer.dim_date
(
pk_date BIGINT NOT NULL ENCODE az64
,iso_date VARCHAR(40) ENCODE zstd
,"year" VARCHAR(16) ENCODE zstd
,quarter_of_year VARCHAR(8) ENCODE zstd
,month_of_year VARCHAR(8) ENCODE zstd
,date_ DATE ENCODE az64
,year_month VARCHAR(6) ENCODE zstd
,id_date BIGINT ENCODE az64
)
DISTSTYLE ALL
这是一个常见但棘手的问题。 JOIN ON 子句中的 BETWEEN 导致在循环中创建大量新行。这个过程非常昂贵,并且这个膨胀的数据集上的下游操作也会受到影响。此外,数据仓库针对数据分析进行了优化,而不是数据创建,因此这种类型的查询在 Redshift 上更加昂贵。
有一种方法可以解决这个问题,使其更加高效,但它需要重新思考如何执行分析,对于大多数分析(但不是全部)而言。性能的提升是惊人的,但解决它会让你的大脑受伤。我为一位客户执行了此操作,在 Redshift 上需要 90 分钟、在大型 EMR 集群上需要 45 分钟的查询被优化为 16 秒。所以这确实有效。
我在这里写了一篇关于进行此优化的教程 - https://wadevelopment.co/sql_limits_wp.html
基本思想是用 UNION ALL 替换 INNER JOIN,同时创建一些新列来检测状态的变化。然后使用滚动总和来获取每日统计数据。我在你的查询中看到的看起来应该是这样的。
我很高兴回答有关此问题的后续问题,但更改为这种方法是重写。