Kendine Katılmaya Alternatif


10

Burada bir soru sordum: /programming/43807566/how-to-divide-two-values-from-the-same-column-but-at-different-rows

değerleri aynı tabloda, aynı sütunda, ancak farklı satırlarda bölme hakkında. Şimdi daha fazla numara ve paydaya sahip olan problemim var (farklı uns). self joinBu sorunu Postgres ile çözmek için hala iyi bir yol var mı yoksa daha iyi çözümler var mı?

Misal:

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

Sonuç şöyle olmalıdır:

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

Değerin posta koduna göre gruplandırıldığı ve formülün (uns ile değer) olduğu durumlarda:

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

Nihai sıfıra bölünmemeye dikkat edin. Formül daha da karmaşık olabilir, ancak bu iyi bir örnektir.


tablonuzda hangi satırların pay ve payda olduğunu gösteren herhangi bir alan var mı?
McNets

bir payda uns 56, 57, 58 ile değerlerinin toplamı
Rastgele

En iyi çözüm, verileri unssütun isimleri haline getirmek için oradan döndürmek olacaktır - oradan, değerleri hangi formül kullanırsa kullanın uygulanabilir olmalıdır. Formül sabit olarak kodlanacak mı yoksa bir şekilde dinamik olarak mı türetilecek?
RDFozz

çok fazla tablo oluşturmak için gereken birkaç formül (~ 30) vardır
Randomize

Yanıtlar:


3

Bu, Michael'ın doğru bir şekilde teşhis edildiği gibi, özünde bir pivot / çapraz tablo problemidir .

tablefuncPostgres'teki modüle aşina değilseniz, buradaki temel talimatları okuyun:

Sorgu basit ve çok hızlı olur (burada sunulan diğer çözümlerden daha hızlıdır):

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 sıfıra bölünmeyi önlemek için.

dbfiddle burada


6

Tüm değer / değer çiftlerini bir JSON nesnesine toplayabilir, ardından bunu UNS değerlerine ada göre erişmek için kullanabilirsiniz. Değerler yalnızca JSON nesnesinden metin olarak alınabileceğinden, bu işlem biraz döküm gerektirir, ancak formül açıklamanıza çok benzer:

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;

Toplama, payda ve bölen değerlendirmesi ve son bölümü daha okunabilir hale getirmek için üç adıma böldüm.

Çevrimiçi örnek: http://rextester.com/IZYT54566


Bir işlev oluşturarak formülü basitleştirebilirsiniz:

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 deseni bunun için işe yarayacaktır. Ortak anahtarlarına göre satır değerlerini tek bir satırdaki sütunlara dönüştürür. Bunu uygulamanın birkaç yolu vardır . Bazıları yalnızca tek bir tablo taraması gerektirir.

PIVOT'tan sonra posta kodu başına bir satır ve değer başına sütun içeren bir tablonuz olur. Sorgunun geri kalanı tek bir tabloya başvuruyormuş gibi yazılır.


3

Varsayarsak (postcode, uns)vardır UNIQUEzaten @ michael-yeşil tarafından yorumladı (muhtemelen, bir PK), PIVOT deseni, uygulanabilir portably aşağıdaki sorguyu kullanarak:

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'da kontrol edin .


3

Muhtemelen optimal olmasa (postcode, uns)da UNIQUE(muhtemelen bir PK), muhtemelen en basit yol, muhtemelen en taşınabilir yol olduğunu varsayarsak : gerektiği kadar alt seçki kullanın :

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 ;

SQLFiddle'da kontrol edin .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.