자가 참여의 대안


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.