使用大表和多个SUM

时间:2017-04-05 02:05:44

标签: sql postgresql sum

我有一个非常大的表,看起来像这样:

event_date (DATE), event_cd (TEXT), bat_id (TEXT)

有几百万行。 type是存储为字符串的数字,表示一种事件类型(' 14',' 16'等等...)。目标是计算给定日期范围和标识符的值。我创建了一个函数,它接受一个标识符,一个开始日期(作为TEXT)和一个结束日期(作为TEXT)并执行以下操作:

CREATE OR REPLACE FUNCTION wOBA_period(player TEXT, start_date TEXT, end_date TEXT)
    RETURNS NUMERIC AS $$
WITH guts AS (SELECT * FROM weights WHERE season = substring(start_date FROM 1 FOR 4) :: INT)
SELECT CASE WHEN sum(CASE WHEN ab_fl = 'T' THEN 1 ELSE 0 END) = 0 THEN NULL ELSE (round(
    ((SELECT wbb FROM guts) * sum(CASE WHEN event_cd = '14' THEN 1 ELSE 0 END)
        + (SELECT whbp FROM guts) * sum(CASE WHEN event_cd = '16' THEN 1 ELSE 0 END)
        + (SELECT w1b FROM guts) * sum(CASE WHEN event_cd = '20' THEN 1 ELSE 0 END)
        + (SELECT w2b FROM guts) * sum(CASE WHEN event_cd = '21' THEN 1 ELSE 0 END)
        + (SELECT w3b FROM guts) * sum(CASE WHEN event_cd = '22' THEN 1 ELSE 0 END)
        + (SELECT whr FROM guts) * sum(CASE WHEN event_cd = '23' THEN 1 ELSE 0 END)) :: NUMERIC
        / (sum(CASE WHEN ab_fl = 'T' THEN 1 ELSE 0 END) + sum(CASE WHEN event_cd = '14' THEN 1 ELSE 0 END)
        + sum(CASE WHEN sf_fl = 'T' THEN 1 ELSE 0 END) + sum(CASE WHEN event_cd = '16' THEN 1 ELSE 0 END)) :: NUMERIC,
    3)) END AS woba
FROM events WHERE bat_id = player AND event_date BETWEEN start_date AND end_date
GROUP BY bat_id;
$$
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;

上下文:它根据某些事件计算wOBA(基于加权平均值)。公式如下:

wOBA = (wBB*BB + wHBP*HBP + w1B*1B + w2B*2B + w3B*3B) / (AB+BB+SF+HBP)

并在events表中,event_cd对应于公式中的每个组件之一(' 20' = 1B,' 14' = BB等)。最后,权重(公式中的wXX)每年都不同,并存储在我为给定年份提取的表格(weights)中,暂时以guts形式提供。

一个问题是这个问题很低。获得wOBA_period函数调用的结果需要大约8秒。当我尝试更新需要为其5000多行中的每一行进行多个函数调用的不同表时,它会成为一个问题。

是否有任何方法可以改进功能以使其更快一点?

由于

示例数据:

来自events

event_date  bat_id      event_cd
2015-05-30  linda001    2
2015-05-30  hellj001    20
2015-05-30  incie001    20
2015-05-30  polla001    2
2015-05-30  goldp001    9
2015-05-30  goldp001    23
2015-05-30  trumm001    21
2015-05-30  hilla001    2
2015-05-30  pachj001    2
2015-05-30  ramia001    2

所以sum(case when event_cd = '20' then 1 else 0 end) group by bat_id会返回次数' 20'出现在给定的bat_id。这是' 1B'的数量。在wOBA公式中。

weights表每年都有这样的一行:

year    wbb     whbp    w1b     w2b     w3b     whr
2016    0.691   0.721   0.878   1.242   1.569   2.015

这些是公式中每个组成部分的权重。

wOBA_period函数需要bat_id和2个日期,并且应该返回一个数字。

1 个答案:

答案 0 :(得分:0)

我当然没有您的数据副本,但您可以尝试以下

  • 尽可能多地创建一个VIEW
  • 使用函数完成工作。

我的想法是功能没有得到很好的优化,特别是如果它们即时创建查询,但视图可能会有所帮助。