自连接的替代方案

Ran*_*ize 10 postgresql pivot computed-column

我在这里问了一个问题:https : //stackoverflow.com/questions/43807566/how-to-divide-two-values-from-the-same-column-but-at-different-rows

关于将同一表中同一列但不同行的值进行除法。现在我的问题是我有更多的分子和分母(不同的uns)。仍然是self join用 Postgres 解决这个问题的好方法还是有更好的解决方案?

例子:

| postcode | value | uns |
|----------|-------|-----|
|       AA |    40 |  53 |
|       BB |    20 |  53 |
|       AA |    10 |  54 |
|       AA |    20 |  55 |
|       AA |    10 |  56 |
|       AA |    30 |  57 |
|       AA |    50 |  58 |
|       BB |    10 |  54 |
|       BB |    10 |  55 |
|       BB |    70 |  56 |
|       BB |    80 |  57 |
|       BB |    10 |  58 |
Run Code Online (Sandbox Code Playgroud)

结果应该是:

| postcode | formula    |
|----------|------------|
|       AA | 18.888...  |
|       BB | 14.375     |
Run Code Online (Sandbox Code Playgroud)

其中值按邮政编码分组,公式为(带有 uns 的值):

(V53 * V56 + V54 * V57 + V55 * V58) / (V56 + V57 + V58)
Run Code Online (Sandbox Code Playgroud)

注意避免最终被零除。公式可能更复杂,但这是一个很好的例子。

a_h*_*ame 6

您可以将所有 uns/value 对聚合到一个 JSON 对象中,然后使用它来按名称访问 UNS 值。这需要进行一些转换,因为值只能从 JSON 对象中提取为文本,但该公式看起来与您的描述非常相似:

with vals(postcode, v) as (
  select postcode, json_object_agg(uns, value)
  from x
  group by postcode
), factors (postcode, denominator, divisor) as (
  select postcode, 
         (v->>'53')::decimal * (v->>'56')::decimal + (v->>'54')::decimal * (v->>'57')::decimal + (v->>'55')::decimal * (v->>'58')::decimal,
         (v->>'56')::decimal + (v->>'57')::decimal + (v->>'58')::decimal
  from vals
)
select postcode, 
       denominator / nullif(divisor, 0)
from factors;
Run Code Online (Sandbox Code Playgroud)

我将聚合、分母和除数的评估以及最终的除法分为三个步骤,以使其更具可读性。

在线示例:http : //rextester.com/IZYT54566


您可以通过创建函数来简化公式:

create function val(p_vals json, p_uns text)
  returns decimal
as $$
  select (p_vals ->> p_uns)::decimal;
$$
language sql;

with vals (postcode, v) as (
  select postcode, json_object_agg(uns, value)
  from x
  group by postcode
), factors (postcode, denominator, divisor) as (
  select postcode, 
         val(v, '53') * val(v, '56') + val(v, '54') * val(v, '57') + val(v, '55') * val(v, '58'),
         val(v, '56') + val(v, '57') + val(v, '58')
  from vals
)
select postcode, 
       denominator / nullif(divisor, 0)
from factors;
Run Code Online (Sandbox Code Playgroud)


Erw*_*ter 3

这是一个核心/交叉表问题的核心,就像迈克尔已经准确诊断的那样。

如果您不熟悉tablefuncPostgres 中的模块,请阅读此处的基本说明:

查询变得简单且非常快(比此处介绍的其他解决方案更快):

SELECT (v53 * v56 + v54 * v57 + v55 * v58) / NULLIF(v56 + v57 + v58, 0)
FROM   crosstab(
   'SELECT postcode, uns, value FROM tbl ORDER BY 1'
 , 'SELECT generate_series(53,58)'
   ) AS ct (postcode text
          , v53 numeric, v54 numeric, v55 numeric
          , v56 numeric, v57 numeric, v58 numeric);
Run Code Online (Sandbox Code Playgroud)

NULLIF以防止被零除。

dbfiddle在这里