Làm cách nào để thực hiện truy vấn con Postgresql trong mệnh đề select với mệnh đề tham gia từ như SQL Server?


81

Tôi đang cố gắng viết truy vấn sau trên postgresql:

select name, author_id, count(1), 
    (select count(1)
    from names as n2
    where n2.id = n1.id
        and t2.author_id = t1.author_id
    )               
from names as n1
group by name, author_id

Điều này chắc chắn sẽ hoạt động trên Microsoft SQL Server nhưng hoàn toàn không hoạt động trên postegresql. Tôi đã đọc tài liệu của nó một chút và có vẻ như tôi có thể viết lại nó thành:

select name, author_id, count(1), total                     
from names as n1, (select count(1) as total
    from names as n2
    where n2.id = n1.id
        and n2.author_id = t1.author_id
    ) as total
group by name, author_id

Nhưng điều đó trả về lỗi sau trên postegresql: "truy vấn con trong FROM không thể tham chiếu đến các quan hệ khác có cùng mức truy vấn". Vì vậy, tôi bị mắc kẹt. Có ai biết làm thế nào tôi có thể đạt được điều đó?

Cảm ơn


Trên thực tế nó có vẻ như điều này sẽ làm việc trên Postgres (có thể 6 năm trước đây nó đã không :))
qwertzguy

Câu trả lời:


121

Tôi không chắc mình hiểu hoàn toàn ý định của bạn, nhưng có lẽ những điều sau đây sẽ gần với những gì bạn muốn:

select n1.name, n1.author_id, count_1, total_count
  from (select id, name, author_id, count(1) as count_1
          from names
          group by id, name, author_id) n1
inner join (select id, author_id, count(1) as total_count
              from names
              group by id, author_id) n2
  on (n2.id = n1.id and n2.author_id = n1.author_id)

Thật không may, điều này thêm yêu cầu nhóm truy vấn con đầu tiên theo id cũng như tên và author_id, điều này tôi không nghĩ là muốn. Tuy nhiên, tôi không chắc làm thế nào để giải quyết vấn đề đó, vì bạn cần có sẵn id để tham gia vào truy vấn con thứ hai. Có lẽ ai đó sẽ đưa ra một giải pháp tốt hơn.

Chia sẻ và tận hưởng.


Bob hoàn hảo, điều đó thực sự hiệu quả. Cảm ơn rất nhiều! Tôi đã phải thực hiện một thay đổi nhỏ vì tôi không cần tham gia với id, chỉ cần tác giả_id. Vì vậy, truy vấn cuối cùng là: select n1.name, n1.author_id, count_1, total_count from (select id, name, author_id, count (1) as count_1 from names group by id, name, author_id) n1 tham gia bên trong (select author_id, count (1) as total_count từ nhóm tên theo author_id) n2 trên (n2.author_id = n1.author_id) Bây giờ tôi có điều này, điều tôi thực sự muốn là chia số lượng_1 cho total_count để có tần suất chuẩn hóa. = D
Ricardo

ops, chỉ cần nhận ra rằng sql không được định dạng đúng ở đây. :( Sẽ đưa ra câu trả lời để bổ sung.
Ricardo

Tôi không gặp sự cố Ricado đang nói nhưng SQL này đã hoàn toàn khắc phục được sự cố của tôi ...: D CẢM ƠN !!!
tftd

15

Bổ sung câu trả lời @Bob Jarvis@dmikam , Postgres không thực hiện một kế hoạch tốt khi bạn không sử dụng LATERAL, bên dưới một mô phỏng, trong cả hai trường hợp, kết quả dữ liệu truy vấn giống nhau, nhưng chi phí rất khác nhau

Cấu trúc bảng

CREATE TABLE ITEMS (
    N INTEGER NOT NULL,
    S TEXT NOT NULL
);

INSERT INTO ITEMS
  SELECT
    (random()*1000000)::integer AS n,
    md5(random()::text) AS s
  FROM
    generate_series(1,1000000);

CREATE INDEX N_INDEX ON ITEMS(N);

Thực hiện JOINvới GROUP BYtrong truy vấn con mà không cóLATERAL

EXPLAIN 
SELECT 
    I.*
FROM ITEMS I
INNER JOIN (
    SELECT 
        COUNT(1), n
    FROM ITEMS
    GROUP BY N
) I2 ON I2.N = I.N
WHERE I.N IN (243477, 997947);

Kết quả

Merge Join  (cost=0.87..637500.40 rows=23 width=37)
  Merge Cond: (i.n = items.n)
  ->  Index Scan using n_index on items i  (cost=0.43..101.28 rows=23 width=37)
        Index Cond: (n = ANY ('{243477,997947}'::integer[]))
  ->  GroupAggregate  (cost=0.43..626631.11 rows=861418 width=12)
        Group Key: items.n
        ->  Index Only Scan using n_index on items  (cost=0.43..593016.93 rows=10000000 width=4)

Sử dụng LATERAL

EXPLAIN 
SELECT 
    I.*
FROM ITEMS I
INNER JOIN LATERAL (
    SELECT 
        COUNT(1), n
    FROM ITEMS
    WHERE N = I.N
    GROUP BY N
) I2 ON 1=1 --I2.N = I.N
WHERE I.N IN (243477, 997947);

Các kết quả

Nested Loop  (cost=9.49..1319.97 rows=276 width=37)
  ->  Bitmap Heap Scan on items i  (cost=9.06..100.20 rows=23 width=37)
        Recheck Cond: (n = ANY ('{243477,997947}'::integer[]))
        ->  Bitmap Index Scan on n_index  (cost=0.00..9.05 rows=23 width=0)
              Index Cond: (n = ANY ('{243477,997947}'::integer[]))
  ->  GroupAggregate  (cost=0.43..52.79 rows=12 width=12)
        Group Key: items.n
        ->  Index Only Scan using n_index on items  (cost=0.43..52.64 rows=12 width=4)
              Index Cond: (n = i.n)

Phiên bản Postgres của tôi là PostgreSQL 10.3 (Debian 10.3-1.pgdg90+1)


3
Cảm ơn vì gợi ý sử dụng LATERAL !!
leole

13

Tôi chỉ trả lời ở đây với phiên bản được định dạng của sql cuối cùng mà tôi cần dựa trên câu trả lời của Bob Jarvis như đã đăng trong nhận xét của tôi ở trên:

select n1.name, n1.author_id, cast(count_1 as numeric)/total_count
  from (select id, name, author_id, count(1) as count_1
          from names
          group by id, name, author_id) n1
inner join (select author_id, count(1) as total_count
              from names
              group by author_id) n2
  on (n2.author_id = n1.author_id)

12

Tôi biết điều này đã cũ, nhưng vì Postgresql 9.3 có một tùy chọn sử dụng từ khóa "LATERAL" để sử dụng truy vấn con LIÊN QUAN bên trong JOINS, vì vậy truy vấn từ câu hỏi sẽ trông giống như sau:

SELECT 
    name, author_id, count(*), t.total
FROM
    names as n1
    INNER JOIN LATERAL (
        SELECT 
            count(*) as total
        FROM 
            names as n2
        WHERE 
            n2.id = n1.id
            AND n2.author_id = n1.author_id
    ) as t ON 1=1
GROUP BY 
    n1.name, n1.author_id

1
Tôi tự hỏi nếu hiệu suất của hai truy vấn này có sự khác biệt, hoặc nếu cho postgresql nó là kế hoạch tương tự
deFreitas


2
select n1.name, n1.author_id, cast(count_1 as numeric)/total_count
  from (select id, name, author_id, count(1) as count_1
          from names
          group by id, name, author_id) n1
inner join (select distinct(author_id), count(1) as total_count
              from names) n2
  on (n2.author_id = n1.author_id)
Where true

được sử dụng distinctnếu nhiều tham gia bên trong hơn, vì nhiều tham gia nhóm hơn thì hiệu suất chậm

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.