Làm cách nào tôi có thể chọn chính xác số lần xuất hiện tối đa của chuỗi trong khi nhóm theo trường khác?


7

Tôi đang sử dụng Postgresql 9.0. Tôi có các trường sau trong một bảng : id, name.

 id    name 
 1     John
 1     Mary
 1     Mary
 1     Mary
 1     John
 1     Mary
 3     Paul
 3     Paul
 3     George
 .     .
 .     .

Đối với mỗi id, tôi muốn chọn tên xảy ra nhiều nhất. Làm thế nào tôi có thể làm điều đó?

Tôi đã thử với truy vấn sau nhưng không được:

select id, max(name) 
from table 
group by id;

2
maxsẽ tìm thấy tên sắp xếp theo từ vựng, không phải là tên được tích lũy nhiều lần nhất. Bạn thực sự muốn một modetổng hợp (theo nghĩa thống kê) cho điều đó, và tôi không nghĩ rằng một cái được cung cấp. Ai đó đã viết một cái trên wiki, nhưng tôi nghi ngờ nó rất hiệu quả. Hãy dùng thử: wiki.postgresql.org/wiki/Aggregate_Modescottrbailey.wordpress.com/2009/05/22/ Kẻ
Craig Ringer

Câu trả lời:


9

Điều này không tầm thường. Đầu tiên, bạn muốn nhóm theo id và tên và đếm các hàng:

SELECT COUNT(*)
...
GROUP BY id, name

Sau đó chọn số lượng tối đa cho mỗi id. Một cách để đạt được điều này là bằng các chức năng của cửa sổ. Các RANK()chức năng:

RANK() OVER (PARTITION BY id ORDER BY COUNT(*) DESC)

gán một số cho mỗi hàng của kết quả (sau khi hoàn thành việc phân nhóm), sắp xếp chúng (các hàng) trong các phân vùng giống nhau idvà được sắp xếp theo COUNT(*) DESC, vì vậy với mỗi (phân vùng) id, các hàng có số lượng tối đa là được chỉ định một cấp bậc 1. Do đó, chúng ta cần đặt ở trên trong một bảng dẫn xuất và sử dụng một WHEREđiều kiện để chỉ giữ các hàng này:

WHERE rnk = 1

Truy vấn cuối cùng là như thế này:

SELECT
    id, name, cnt
FROM
    ( SELECT id, name, COUNT(*) AS cnt,
             RANK() OVER (PARTITION BY id ORDER BY COUNT(*) DESC) AS rnk
      FROM tableX
      GROUP BY id, name
    ) AS tg 
WHERE
    rnk = 1 ;

Đã thử nghiệm tại SQL-Fiddle


Lưu ý rằng nếu bạn có quan hệ ở vị trí đầu tiên (hai hoặc nhiều tên có cùng số lượng tối đa), tất cả những tên này sẽ được trả về. Nếu bạn muốn đúng một hàng cho mỗi id trong kết quả cuối cùng, bạn phải sử dụng ROW_NUMBER()thay vì RANK()và có thể thay đổi ORDER BYmệnh đề để chọn rõ ràng cách giải quyết các mối quan hệ:

ROW_NUMBER() OVER (PARTITION BY id ORDER BY COUNT(*) DESC, name ASC) AS rnk

Đã kiểm tra: SQL-Fiddle test-2 .


Điều này có đơn giản hơn với một số CTE trung gian không? Hay điều đó sẽ ảnh hưởng xấu đến hiệu suất?
tự đại diện

1
Các CTE @Wildcard được cụ thể hóa trong Postgres, trong khi các bảng dẫn xuất cung cấp nhiều tùy chọn hơn cho trình tối ưu hóa.
ypercubeᵀᴹ

Cảm ơn! Nhưng ý nghĩa chính xác của "bảng dẫn xuất" là gì? Tôi chưa gặp thuật ngữ đó trước đây.
tự đại diện

1
Bảng dẫn xuất là một truy vấn con có tên / bí danh xuất hiện trong một FROMmệnh đề. Những gì bên trong dấu ngoặc đơn trong mã của tôi (được đặt tên tg)
ypercubeᵀᴹ
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.