# 自助加入的替代方法

10

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

``````| postcode | formula    |
|----------|------------|
|       AA | 18.888...  |
|       BB | 14.375     |``````

``(V53 * V56 + V54 * V57 + V55 * V58) / (V56 + V57 + V58)``

McNets '17

RDFozz

3

``````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);``````

`NULLIF` 防止被零除。

dbfiddle 在这里

6

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

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

4

PIVOT模式将对此有效。它将根据其公共键将行的值转换为单行中的列。有几种方法可以实现此目的。有些只需要单个表扫描。

3

``````SELECT
postcode,
CAST(V53 * V56 + V54 * V57 + V55 * V58 AS numeric)
/ nullif(V56 + V57 + V58, 0) AS formula
FROM
(SELECT
postcode,
sum(case when uns=53 then value end) AS v53,
sum(case when uns=54 then value end) AS v54,
sum(case when uns=55 then value end) AS v55,
sum(case when uns=56 then value end) AS v56,
sum(case when uns=57 then value end) AS v57,
sum(case when uns=58 then value end) AS v58
FROM
t
GROUP BY
postcode
) AS s
ORDER BY
postcode ;``````

SQLFiddle上检查它。

3

``````SELECT
postcode,
((SELECT value FROM t WHERE t.uns = 53 AND t.postcode = p.postcode) *
(SELECT value FROM t WHERE t.uns = 56 AND t.postcode = p.postcode) +
(SELECT value FROM t WHERE t.uns = 54 AND t.postcode = p.postcode) *
(SELECT value FROM t WHERE t.uns = 57 AND t.postcode = p.postcode) +
(SELECT value FROM t WHERE t.uns = 55 AND t.postcode = p.postcode) *
(SELECT value FROM t WHERE t.uns = 58 AND t.postcode = p.postcode)
)::double precision /
nullif( (SELECT sum(value) FROM t
WHERE t.uns IN (56, 57, 58) AND t.postcode = p.postcode), 0)
AS formula
FROM
(SELECT DISTINCT postcode FROM t) AS p
ORDER BY
postcode ;``````