【发布时间】:2013-12-19 21:09:58
【问题描述】:
我在使用一组 SQL 来实时生成当前月份的语句时遇到性能问题。
客户会使用在线系统的积分购买一些商品,应生成包含“open_balance”、“point_earned”、“point_used”、“current_balance”的语句。
以下显示了缩短的架构:
//~200k records
customer: {account_id:string, create_date:timestamp, bill_day:int} //totally 14 fields
//~250k records per month, kept for 6 month
history_point: {point_id:long, account_id:string, point_date:timestamp, point:int} //totally 9 fields
//each customer have maximum of 12 past statements kept
history_statement: {account_id:string, open_date:date, close_date:date, open_balance:int, point_earned:int, point_used:int, close_balance:int} //totally 9 fields
在每个账单日,视图应自动创建新的月份报表。 即如果 bill_day 为 15,则在 2013 年 12 月 16 日 00:00:00 或之后完成的交易应属于 2013 年 12 月 16 日 00:00:00 - 2014 年 1 月 15 日 23:59:59 的新账单周期
我尝试了下面描述的方法,
- 计算每个帐户的最后关闭日(在具体化视图中,以便仅在将新客户或过去一个月的对帐单插入 history_statement 后更新)
- 每个月为每个客户生成我需要计算的记录(也在物化视图中)
- 在我将计算的日期内仅筛选点记录(仅需要约 0.1 秒)
- 将 2 与 3 相结合,以获得每个客户每月赚取和使用的积分
- 将 4 与 4 在日期小于未结日期的日期相加,得出期初余额和期末余额 6a。从开放日期小于 1 个月的 5 中选择当前余额(这些尚未关闭,该点反映每个客户现在拥有的点) 6b。所有的语句都是由history_statement和5的并集得到的
在开发服务器上,平均响应时间(20 万客户,当月 150 万次交易)约为 3 秒,这对于 Web 应用程序来说相当慢,而在可能共享资源的测试服务器上,平均响应时间响应时间(20 万客户,8 个月内每月约 20 万笔交易)为 10-15 秒。
有人对用更好的方法编写查询或加快查询有一些想法吗?
相关SQL:
2:IV_STCLOSE_2_1_T(实体化视图)
3:IV_STCLOSE_2_2_T(~0.15s)
SELECT ACCOUNT_ID, POINT_DATE, POINT
FROM history_point
WHERE point_date >= (
SELECT MIN(open_date)
FROM IV_STCLOSE_2_1_t
)
4:IV_STCLOSE_3_T(~1.5s)
SELECT p0.account_id, p0.open_date, p0.close_date, COALESCE(SUM(DECODE(SIGN(p.point),-1,p.point)),0) AS point_used, COALESCE(SUM(DECODE(SIGN(p.point),1,p.point)),0) AS point_earned
FROM iv_stclose_2_1_t p0
LEFT JOIN iv_stclose_2_2_t p
ON p.account_id = p0.account_id
AND p.point_date >= p0.open_date
AND p.point_date < p0.close_date + INTERVAL '1' DAY
GROUP BY p0.account_id, p0.open_date, p0.close_date
5:IV_STCLOSE_4_T (~3s)
WITH t AS (SELECT * FROM IV_STCLOSE_3_T)
SELECT t1.account_id AS STAT_ACCOUNT_ID, t1.open_date, t1.close_date, t1.open_balance, t1.point_earned AS point_earn, t1.point_used , t1.open_balance + t1.point_earned + t1.point_used AS close_balance
FROM (
SELECT v1.account_id, v1.open_date, v1.close_date, v1.point_earned, v1.point_used, COALESCE(sum(v2.point_used + v2.point_earned),0) AS OPEN_BALANCE
FROM t v1
LEFT JOIN t v2
ON v1.account_id = v2.account_id
AND v1.OPEN_DATE > v2.OPEN_DATE
GROUP BY v1.account_id, v1.open_date, v1.close_date, v1.point_earned, v1.point_used
) t1
【问题讨论】:
-
一些备注:1) 查看实际使用的查询可能会有所帮助 2) 您应该在该查询(或多个查询)上运行
EXPLAIN PLAN并显示该命令的结果 -
@fvu 相关策略的SQL在文章底部。