자가 참여의 대안


10

나는 여기에 질문을했다 : https : //.com/questions/43807566/how-to-divide-two-values-from-the-same-column-but-at-different-rows

같은 테이블의 값을 같은 열에서 다른 행으로 나누는 것. 이제 더 많은 분자와 분모가있는 문제가 있습니다 (와 다른 uns). 는 여전히 self join포스트 그레스 또는 더 나은 해결책이 있습니다이 문제를 해결하는 좋은 방법?

예:

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

값이 우편 번호로 그룹화되고 수식이 (uns 값) 인 경우 :

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

결과적으로 0으로 나누지 않도록주의하십시오. 공식은 훨씬 더 복잡 할 수 있지만 좋은 예입니다.


테이블에 어떤 행이 분자와 분모인지 표시하는 필드가 있습니까?
— McNets

아니오, 분모는 uns 56, 57, 58의 값의 합입니다.
— Randomize

가장 좋은 해결책은 데이터를 피벗하여 uns열 이름이 되도록하는 것입니다. 거기에서 값을 사용하는 모든 수식이 작동 가능해야합니다. 수식이 하드 코딩되거나 동적으로 파생됩니까?
— RDFozz

너무 많은 테이블을 만드는 데 필요한 몇 가지 수식 (~ 30)이 있습니다.
— Randomize

답변:


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

NULLIF 0으로 나누기를 방지합니다.

여기 dbfiddle


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;

나는 집계, 분모 및 제수의 평가 및 최종 나누기를 더 읽기 쉽도록 세 단계로 나누었습니다.

온라인 예 : 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;

4

PIVOT 패턴이이를 위해 작동합니다. 공통 키에 따라 행 값을 단일 행의 열로 변환합니다. 이를 구현 하는 몇 가지 방법 이 있습니다. 일부는 단일 테이블 스캔 만 필요합니다.

PIVOT 후에는 우편 번호 당 하나의 행과 값당 열이있는 테이블이 있습니다. 나머지 쿼리는 단일 테이블을 참조하는 것처럼 작성됩니다.


3

@ michael-green이 이미 언급 한 것처럼 PIVOT 패턴 (postcode, uns)은 UNIQUE(아마도 PK) 가정하면 다음 쿼리를 사용하여 이식 가능하게 구현할 수 있습니다 .

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

그 가정 (postcode, uns)된다 UNIQUE, 아마 (아마하는 PK) 간단 하지 최적 비록 아마, 방법, 아마도 대부분의 휴대용 하나를 : 많은 부속 선택 등의 사용을 필요로 :

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을 확인하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.