Xóa các hàng trùng lặp với hai hoặc nhiều bảng tham gia bên trái


7

Với một truy vấn như thế này

SELECT a.id, a.name,
       COALESCE( json_agg(b.*), '[]'::json ),
       COALESCE( json_agg(c.*), '[]'::json ),
  FROM a
  LEFT JOIN b ON a.id = b.a_id
  LEFT JOIN c ON a.id = c.a_id
 GROUP BY a.id, a.name;

Khi được thực thi, cả hai cbsẽ được nhân với nhau và tạo ra các mục trùng lặp trong đối tượng mảng JSON.

Tôi đã thử thay đổi truy vấn để sử dụng 2 truy vấn con thay thế, nhưng tôi nhận được tất cả các loại lỗi và cảnh báo, chẳng hạn như "Truy vấn con chỉ phải trả về một cột", v.v.

Tôi cũng đã thử sử dụng LEFT OUTER JOIN, nhưng tôi đoán tôi chưa nắm vững cách thức các bảng tham gia hoạt động vì nó chỉ áp dụng cho bcvẫn được nhân lên và chứa các bản sao.

Chỉnh sửa : sử dụng các DISTINCTlỗi với "không thể xác định toán tử đẳng thức cho kiểu json" trên các COALESCEhàm.

Làm cách nào tôi có thể sửa truy vấn này và chỉ tổng hợp các hàng duy nhất?


Chỉnh sửa 2

Tôi cần xác định rằng cả hai bảng bcthực sự VIEWlà s và cả hai đều có ít nhất một json_aggcột, vì vậy tôi không thể chỉ sử dụng json_agg(DISTINCT b.*). Điều này đã trở nên quá dễ dàng.


Chỉnh sửa 3

Đây là một đoạn nhỏ để tái tạo vấn đề:

--DROP TABLE IF EXISTS tbl_a CASCADE;
--DROP TABLE IF EXISTS tbl_b CASCADE;
--DROP TABLE IF EXISTS tbl_c CASCADE;

CREATE TABLE tbl_a (
  id bigserial NOT NULL,
  name character varying(16),
  CONSTRAINT "PK_tbl_a" PRIMARY KEY (id)
) WITH ( OIDS=FALSE );

CREATE TABLE tbl_b (
  a_id bigint NOT NULL,
  foo json NOT NULL DEFAULT '{}'::json,
  CONSTRAINT "FK_tbl_b_a" FOREIGN KEY (a_id)
      REFERENCES tbl_a (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
) WITH ( OIDS=FALSE );

CREATE TABLE tbl_c (
  a_id bigint NOT NULL,
  bar json NOT NULL DEFAULT '{}'::json,
  CONSTRAINT "FK_tbl_c_a" FOREIGN KEY (a_id)
      REFERENCES tbl_a (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
) WITH ( OIDS=FALSE );

INSERT INTO tbl_a (id,name) VALUES (1, 'Test');
INSERT INTO tbl_b (a_id, foo) VALUES (1, '{"foo":"Hello"}'::json), (1, '{"foo":"World"}'::json);
INSERT INTO tbl_c (a_id, bar) VALUES (1, '{"bar":"abc"}'::json), (1, '{"bar":"def"}'::json);

SELECT tbl_a.id, tbl_a.name,
       COALESCE(json_agg(tbl_b.*), '{}'::json),
       COALESCE(json_agg(tbl_c.*), '{}'::json)
  FROM tbl_a
  LEFT JOIN tbl_b ON tbl_a.id = tbl_b.a_id
  LEFT JOIN tbl_c ON tbl_a.id = tbl_c.a_id
 GROUP BY tbl_a.id, tbl_a.name;

Trả về

id  name    coalesce                              coalesce
--  ------  ------------------------------------  ----------------------
 1  "Test"  "[{"a_id":1,"foo":{"foo":"World"}},   "[{"a_id":1,"bar":{"bar":"abc"}},
             {"a_id":1,"foo":{"foo":"Hello"}},    {"a_id":1,"bar":{"bar":"abc"}}, 
             {"a_id":1,"foo":{"foo":"World"}},    {"a_id":1,"bar":{"bar":"def"}}, 
             {"a_id":1,"foo":{"foo":"Hello"}}]"   {"a_id":1,"bar":{"bar":"def"}}]"

Câu trả lời:


5

Tôi đã tìm thấy một giải pháp. Tôi không chắc chắn nếu nó là tối ưu, nhưng nó hoạt động.

SELECT tbl_a.id, tbl_a.name,
       COALESCE( ( SELECT json_agg(tbl_b.*)
                     FROM tbl_b 
                    WHERE tbl_b.a_id = tbl_a.id ), '{}'::json),
       COALESCE( ( SELECT json_agg(tbl_c.*)
                     FROM tbl_c 
                    WHERE tbl_c.a_id = tbl_a.id ), '{}'::json)
  FROM tbl_a;

Trả về đúng

id  name    coalesce                             coalesce
--  ------  -----------------------------------  ----------------------
 1  "Test"  "[{"a_id":1,"foo":{"foo":"World"}},  "[{"a_id":1,"bar":{"bar":"abc"}},
              {"a_id":1,"foo":{"foo":"Hello"}},    {"a_id":1,"bar":{"bar":"def"}}]"

4

Một tùy chọn khác là sử dụng các bảng dẫn xuất:

SELECT
  a.id,
  a.name,
  COALESCE(b.json_data, '{}'::json) AS b_json,
  COALESCE(c.json_data, '{}'::json) AS c_json
FROM
  tbl_a AS a
  LEFT JOIN
    (
      SELECT
        a_id,
        json_agg(*) AS json_data
      FROM
        tbl_b
      GROUP BY
        a_id
    ) AS b ON b.a_id = a.id
  LEFT JOIN
    (
      SELECT
        a_id,
        json_agg(*) AS json_data
      FROM
        tbl_c
      GROUP BY
        a_id
    ) AS c ON c.a_id = a.id
;

Trong trường hợp này, tbl_btbl_cđược tổng hợp trước khi chúng được tham gia tbl_a. Do kết quả của việc nhóm a_id, không bảng dẫn xuất nào có a_idcác mục trùng lặp tại thời điểm tham gia. Điều đó ngăn các liên kết sản xuất các sản phẩm Cartesian nhỏ - hiệu ứng bạn đã gặp phải với truy vấn ban đầu của mình.


Cả hai chạy cùng một lúc, nhưng đó là một mẹo hay! Chúc mừng!
Yanick Rochon
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.