【问题标题】:Division by zero and CASE WHEN function除以零和 CASE WHEN 函数
【发布时间】:2018-03-11 19:18:49
【问题描述】:

我有一张这样的桌子:

|Date |Type |MetricA|MetricB|
|01-01|House|0      |500    |
|01-01|Paid |1      |1000   |
|01-01|Paid |1      |4000   |
|02-01|House|0      |3000   |
|02-01|Paid |10     |13000  |
|02-01|House|0      |5000   |
|02-01|Paid |5      |10000  |
|02-01|Paid |1      |1500   |

我想计算第三个指标 (1000*MetricA / MetricB),我还希望 MetricB 按类型拆分,这样我的最终表格如下所示:

|Date |Metric_House|Metric_Paid|MetricA|NewMetric
|01-01|500         |5000       |2      |0.40
|02-01|8000        |24500      |16     |0.653061224

(我希望表结构有意义,否则我可以在这里给你截图:https://i.imgur.com/5HZiMh8.png

我想出了如何正确编写 Metric_House、Metric_Paid、MetricA 列,但我正在努力编写 NewMetric。

有时 MetricB == 0,然后我可以除以 0。我可以编写一个 case 函数,但是我遇到了聚合求和的麻烦。我花了几个小时尝试为此编写查询,但我无法弄清楚:( 我试过这个:

func.sum(case([((i.c.metricb) != 0, 1000*func.sum(i.c.metrica)/i.c.metricb)], else_=0))

这是 SQLAlchemy(SQL 之上的 Python 层),但它基本上是:

SUM(CASE WHEN METRIC != 0 THEN 1000*SUM(METRICA)/SUM(METRICB) ELSE 0

在该查询之后我得到一个嵌套错误。

我在 Python 框架中工作,当我忽略查询中的 NewMetric 列并随后使用简单的列表函数添加它时,我在几分钟内就完成了。但我宁愿在 SQL 内部而不是外部处理数据。 编写此查询的最佳方式是什么?提前致谢!

编辑:

这是我的工作:

stmt = select([i.c.date,
cast(func.sum(case([(
i.c.type != 'House', i.c.metricb)], else_=0)), 
Integer).label('metric_paid'),
cast(func.sum(case([(
i.c.type == 'House', i.c.metricb)], else_=0)),                            
Integer).label('metric_house'),
cast(func.sum(i.c.metrica), Float).label('metrica')]).group_by(
i.c.date).order_by(asc(i.c.date))

现在这是我尝试过的(添加这个额外的表达式):

case([(sum(i.c.metricb) != 0, 
1000*sum(i.c.metrica)/sum(i.c.metricb))], else_=0)])

NotImplementedError:此表达式不支持运算符“getitem”

func.sum(case([(i.c.metricb!= 0, 
1000*func.sum(i.c.metrica)/func.sum(i.c.metricb))], else_=0))

(psycopg2.ProgrammingError) 聚合函数调用不能嵌套 LINE 1: ...i.metricb != 0) THEN (1000 * sum(i...

更多的尝试和错误:

case([(and_(func.sum(i.c.metricb) != 0, (i.c.type) != 'House'), 1000*func.sum(i.c.metrica)/func.sum(i.c.metricb))])

ProgrammingError: (psycopg2.ProgrammingError) 列“type”必须出现在 GROUP BY 子句中或用于聚合函数中

但是我不想要group by子句中的type列,那不会产生错误的结果

【问题讨论】:

  • 做一个 GROUP BY。在选择列表中使用案例表达式来选择每个“类型”。还有另一种避免被零除的情况。
  • “我在那个查询之后得到一个嵌套错误。”这意味着什么?解释器肯定会引发异常并向您显示堆栈跟踪,您应该在问题中提供此信息。
  • 感谢 jarlh 的回答。我使用 group by 作为日期。我还需要分组什么? Giacomo,我得到的错误是 ProgrammingError: (psycopg2.ProgrammingError) 聚合函数调用不能嵌套 LINE 1: ...i.metricb != 0) THEN (1000 * sum(i...
  • 您实际上拥有SUM(SUM()),因此您会收到错误消息。与 CASE 无关。请生成minimal reproducible example,或者换句话说,包括相关模型/表的相关部分以及您正在使用的实际查询语句。 Python 构造与您提供的 SQL 不匹配。还包括问题中的回溯。您已标记此 mysql,但回溯 sn-p 提示 Postgresql...
  • 糟糕,你是对的,我在 postgresql 中工作,而不是 mysql;我已经在标记中更改了这一点,抱歉。我还编辑了原始问题并提供了我正在使用的陈述。感谢您的帮助!

标签: python sql postgresql sqlalchemy


【解决方案1】:

给你,以下工作:

WITH mytable AS (
    SELECT
        *
    FROM (
        VALUES
            ('2018-01-01', 'House', 0, 500),
            ('2018-01-01', 'Paid', 1, 1000),
            ('2018-01-01', 'Paid', 1, 4000),
            ('2018-01-02', 'House', 0, 3000),
            ('2018-01-02', 'Paid', 10, 13000),
            ('2018-01-02', 'House', 0, 5000),
            ('2018-01-02', 'Paid', 5, 10000),
            ('2018-01-02', 'Paid', 1, 1500)
    ) AS t(date, type, metric_a, metric_b)
)
-- you dont need the above CTE, it is there just for testing in place of your table; use just the select below
SELECT
    date,
    sum(metric_b) FILTER (WHERE type = 'House') AS metric_house,
    sum(metric_b) FILTER (WHERE type = 'Paid') AS metric_paid,
    sum(metric_a) AS metric_a,
    CASE
        WHEN sum(metric_b) FILTER (WHERE type = 'Paid') > 0
            THEN round(1000.0 * sum(metric_a) / sum(metric_b) FILTER (WHERE type = 'Paid'), 4)
        ELSE 0
    END AS new_metric
FROM mytable
GROUP BY date
ORDER BY date;

这给出了:

| date       | metric_house | metric_paid | metric_a | new_metric | 
|------------|--------------|-------------|----------|------------| 
| 2018-01-01 | 500          | 5000        | 2        | 0.4        | 
| 2018-01-02 | 8000         | 24500       | 16       | 0.6531     | 

另外,您对 metric_new 的定义不清楚。根据您发布的结果,您实际上不想除以整个 metric_b,而只是除以其中的“付费”部分。

【讨论】:

  • 嗨乔。非常感谢您回答这个问题。不幸的是,我无法让它工作。尝试此操作时出现语法错误: ProgrammingError: (psycopg2.ProgrammingError) syntax error at or near "(" LINE 18: sum(metricb) FILTER (WHERE type....... 编辑:我正在运行 postgresql 9.3 ,而且这个功能似乎在 9.4 中已经引入。
  • 编辑:我正在运行 postgresql 9.3,并且似乎在 9.4 中引入了此功能。我用 CASE WHEN 语句替换了 FILTER 语句,它起作用了。非常感谢!!
  • 嗨 Captivader,很高兴听到它对您有所帮助。所以如果你的问题解决了,请采纳这个答案,让大家看到这个问题已经解决了。
  • 我想这是礼仪吗?我是新来的。再次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多