除以零和CASE WHEN功能

时间:2018-03-09 08:49:54

标签: python sql postgresql sqlalchemy

我有一张桌子:

|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   |

我想计算第3个指标(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.我可以写一个案例函数,但后来遇到聚合求和问题。我花了几个小时尝试为此写一个查询,我只是无法弄明白:( 我试过这个:

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列,不会产生错误的结果

1 个答案:

答案 0 :(得分:0)

在这里,您可以进行以下工作:

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,而只需要按"付费"部分内容。