【问题标题】:Oracle SQL Month Statement GenerationOracle SQL 月语句生成
【发布时间】: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 的新账单周期

我尝试了下面描述的方法,

  1. 计算每个帐户的最后关闭日(在具体化视图中,以便仅在将新客户或过去一个月的对帐单插入 history_statement 后更新)
  2. 每个月为每个客户生成我需要计算的记录(也在物化视图中)
  3. 在我将计算的日期内仅筛选点记录(仅需要约 0.1 秒)
  4. 将 2 与 3 相结合,以获得每个客户每月赚取和使用的积分
  5. 将 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在文章底部。

标签: sql oracle11g


【解决方案1】:

原来是在IV_STCLOSE_4_T

WITH t AS (SELECT * FROM IV_STCLOSE_3_T)

有问题。

起初认为WITH t AS 会更快,因为 IV_STCLOSE_3_T 只评估一次,但它显然强制实现整个 IV_STCLOSE_3_T,生成超过 20 万条记录,尽管我在任何时候只需要来自单个客户的最多 12 条记录。

删除上述语句并适当索引 account_id,成本从超过 500k 减少到不到 500。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-08
    • 1970-01-01
    相关资源
    最近更新 更多