Alternatif untuk Bergabung Sendiri


10

Saya telah mengajukan pertanyaan di sini: /programming/43807566/how-to-divide-two-values-from-the-same-column-but-at-different-rows

tentang membagi nilai dari tabel yang sama, pada kolom yang sama tetapi pada baris yang berbeda. Sekarang saya memiliki masalah di mana saya memiliki lebih banyak pembilang dan penyebut (dengan berbeda uns). Apakah masih self joincara yang baik untuk menyelesaikan masalah ini dengan Postgres atau ada solusi yang lebih baik?

Contoh:

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

Hasilnya harus:

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

Di mana nilai dikelompokkan berdasarkan kode pos dan rumusnya (nilai dengan uns):

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

Membayar perhatian untuk menghindari pembagian akhirnya dengan nol. Formula bisa menjadi lebih rumit tetapi itu adalah contoh yang bagus.


apakah ada bidang di meja Anda yang menandai baris mana yang merupakan pembilang dan penyebut?
McNets

tidak, penyebut adalah penjumlahan nilai dengan uns 56, 57, 58.
Acak

Kedengarannya seperti solusi terbaik adalah dengan memutar data sehingga unsmenjadi nama kolom - dari sana, formula apa pun yang menggunakan nilai harus dapat diterapkan. Apakah formula tersebut akan dikodekan secara keras, atau diturunkan secara dinamis entah bagaimana?
RDFozz

ada beberapa rumus (~ 30) yang diperlukan untuk membuat tabel terlalu banyak
Acak

Jawaban:


3

Ini adalah masalah pivot / crosstab , seperti Michael yang telah didiagnosis secara akurat.

Jika Anda tidak terbiasa dengan tablefuncmodul di Postgres, baca instruksi dasar di sini:

Permintaan menjadi sederhana dan sangat cepat (lebih cepat dari solusi lain yang disajikan di sini):

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 untuk mencegah pembagian dengan nol.

Aku di sini


6

Anda bisa menggabungkan semua pasangan uns / value menjadi objek JSON, lalu menggunakannya untuk mengakses nilai UNS dengan nama. Ini membutuhkan beberapa casting karena nilainya hanya dapat diekstraksi sebagai teks dari objek JSON, tetapi rumusnya terlihat sangat mirip dengan deskripsi Anda kemudian:

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;

Saya telah membagi agregasi, evaluasi penyebut dan pembagi dan pembagian akhir menjadi tiga langkah untuk membuatnya lebih mudah dibaca.

Contoh online: http://rextester.com/IZYT54566


Anda dapat menyederhanakan rumus dengan membuat fungsi:

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

Pola PIVOT akan bekerja untuk ini. Itu mengkonversi nilai baris ke kolom dalam satu baris, sesuai dengan kunci bersama mereka. Ada beberapa cara untuk mengimplementasikannya. Beberapa hanya membutuhkan satu pemindaian tabel.

Setelah PIVOT Anda akan memiliki tabel dengan satu baris per kode pos dan kolom per nilai. Sisa kueri akan ditulis seolah-olah mereferensikan satu tabel.


3

Dengan asumsi bahwa (postcode, uns)yang UNIQUE(mungkin, seorang PK), pola PIVOT, sebagaimana telah berkomentar oleh @ michael-hijau, dapat diimplementasikan portable menggunakan query berikut:

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 ;

Periksa di SQLFiddle .


3

Dengan asumsi bahwa (postcode, uns)yang UNIQUE(mungkin, PK a), mungkin paling sederhana cara, mungkin yang paling portabel satu, meskipun mungkin tidak optimal: digunakan sebagai banyak subselects yang diperlukan :

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 ;

Periksa di SQLFiddle .

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.