Thay thế cho tự tham gia


10

Tôi đã hỏi một câu hỏi ở đây: /programming/43807566/how-to-divide-two-values-from-the-same-column-but-at-different-rows

về việc chia các giá trị từ cùng một bảng, tại cùng một cột nhưng trên các hàng khác nhau. Bây giờ tôi có vấn đề là tôi có nhiều tử số và mẫu số hơn (với khác nhau uns). Vẫn là self joinmột cách tốt để giải quyết vấn đề này với Postgres hoặc có giải pháp nào tốt hơn?

Thí dụ:

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

Kết quả sẽ là:

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

Trong đó giá trị được nhóm theo mã bưu điện và công thức là (giá trị với uns):

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

Chú ý để tránh phân chia cuối cùng bằng không. Công thức có thể thậm chí phức tạp hơn nhưng đó là một ví dụ tốt.


Có trường nào trên bảng của bạn gắn cờ các hàng là tử số và mẫu số không?
McNets

không, mẫu số là tổng của các giá trị với số 56, 57, 58.
ngẫu nhiên

Âm thanh như giải pháp tốt nhất sẽ là xoay vòng dữ liệu để unstrở thành tên cột - từ đó, bất kỳ công thức nào sử dụng các giá trị sẽ trở nên khả thi. Công thức sẽ được mã hóa cứng, hoặc có nguồn gốc động bằng cách nào đó?
RDFozz

có một vài công thức (~ 30) sẽ tạo ra quá nhiều bảng
ngẫu nhiên

Câu trả lời:


3

Đây là một vấn đề trục / crosstab ở cốt lõi của nó, giống như Michael đã được chẩn đoán chính xác.

Nếu bạn không quen thuộc với tablefuncmô-đun trong Postgres, hãy đọc hướng dẫn cơ bản tại đây:

Truy vấn trở nên đơn giản và rất nhanh (nhanh hơn các giải pháp khác được trình bày ở đây):

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 để ngăn chia cho số không.

dbfiddle ở đây


6

Bạn có thể tổng hợp tất cả các cặp uns / value thành một đối tượng JSON, sau đó sử dụng nó để truy cập các giá trị UNS theo tên. Điều này yêu cầu một số lần truyền vì các giá trị chỉ có thể được trích xuất dưới dạng văn bản từ đối tượng JSON, nhưng công thức trông rất giống với mô tả của bạn sau đó:

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;

Tôi đã chia tổng hợp, đánh giá mẫu số và ước số và phân chia cuối cùng thành ba bước để làm cho nó dễ đọc hơn.

Ví dụ trực tuyến: http://rextester.com/IZYT54566


Bạn có thể đơn giản hóa công thức bằng cách tạo một hàm:

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

Mẫu PIVOT sẽ hoạt động cho việc này. Nó chuyển đổi các giá trị của hàng thành các cột trong một hàng, theo khóa chung của chúng. Có một vài cách để thực hiện điều này. Một số chỉ yêu cầu quét bảng duy nhất.

Sau PIVOT, bạn sẽ có một bảng với một hàng cho mỗi mã bưu điện và một cột cho mỗi giá trị. Phần còn lại của truy vấn sẽ được viết như thể nó tham chiếu một bảng duy nhất.


3

Giả sử (postcode, uns)được UNIQUE(có lẽ, một PK), mô hình PIVOT, như đã nhận xét bởi @ michael-màu xanh lá cây, có thể được thực hiện portably bằng cách sử dụng truy vấn sau đây:

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 ;

Kiểm tra nó tại SQLFiddle .


3

Giả sử (postcode, uns)được UNIQUE(có lẽ, một PK), có lẽ là đơn giản nhất bằng cách nào, có lẽ là một di nhất, mặc dù có lẽ không phải là tối ưu: sử dụng như nhiều subselects khi cần thiết :

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 ;

Kiểm tra tại SQLFiddle .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.