2010-10-05 28 views
11

Tôi đang cố gắng viết một truy vấn trong Postgresql để lấy một tập dữ liệu được sắp xếp và lọc nó theo một trường riêng biệt. Tôi cũng cần phải kéo một số lĩnh vực khác từ cùng một hàng bảng, nhưng họ cần phải được bỏ ra khỏi đánh giá riêng biệt. Ví dụ:Sử dụng mệnh đề DISTINCT để lọc dữ liệu nhưng vẫn kéo các trường khác không phải là DISTINCT

SELECT DISTINCT(user_id) user_id, 
     created_at 
    FROM creations 
ORDER BY created_at 
    LIMIT 20 

Tôi cần user_idDISTINCT, nhưng không quan tâm cho dù ngày created_at là duy nhất hay không. Bởi vì ngày created_at được đưa vào đánh giá, tôi nhận được bản sao user_id trong tập kết quả của mình.

Ngoài ra, dữ liệu phải được sắp xếp theo ngày, do đó, sử dụng DISTINCT ON không phải là một tùy chọn tại đây. Nó yêu cầu trường DISTINCT ON là trường đầu tiên trong mệnh đề ORDER BY và điều đó không cung cấp kết quả mà tôi tìm kiếm.

Làm cách nào để sử dụng đúng điều khoản DISTINCT nhưng giới hạn phạm vi của nó thành chỉ một trường trong khi vẫn chọn các trường khác?

+0

Khái niệm 'DISTINCT' vốn đã áp dụng cho tất cả các cột được chọn vì nếu không sẽ có một tập hợp vốn có ... do đó chức năng' GROUP'. Bạn đang xem loại kết quả nào? Bạn có thể đưa ra một ví dụ về những gì dữ liệu có thể nắm giữ và kết quả bạn muốn? – Matthew

+0

Điều thú vị (và không liên quan đến câu trả lời) là tôi đã chạy qua nỗ lực trên để có 'DISTINCT (cột1), cột2' hoạt động trên một cột đơn. Tuy nhiên, các cơ sở dữ liệu phân tích nó thành 'DISTINCT column1, column2' - may mắn cho bạn nó trả về kết quả sai - nếu không nó có thể đã cắn bạn nhiều hơn sau đó (lần đầu tiên tôi thấy nó nằm trong cơ sở dữ liệu sản xuất). – Unreason

+0

"dữ liệu phải được sắp xếp theo ngày" - ** mà ** ngày? Ngày created_at sớm nhất cho mỗi người dùng? Mới nhất? Thứ gì khác? –

Trả lời

5

Như bạn đã phát hiện, xử lý SQL chuẩn DISTINCT như áp dụng cho toàn bộ danh sách lựa chọn, chứ không phải chỉ là một cột hoặc một vài cột. Lý do cho điều này là nó là mơ hồ những gì giá trị để đưa vào các cột bạn loại trừ từ DISTINCT. Vì lý do tương tự, SQL chuẩn không cho phép bạn có các cột không rõ ràng trong một truy vấn với GROUP BY.

Nhưng PostgreSQL có phần mở rộng không chuẩn cho SQL để cho phép những gì bạn đang yêu cầu: DISTINCT ON (expr).

SELECT DISTINCT ON (user_id) user_id, created_at 
FROM creations 
ORDER BY user_id, created_at 
LIMIT 20 

Bạn phải bao gồm (các) biểu thức riêng biệt như phần tận cùng bên trái của mệnh đề ORDER BY của bạn.

Xem hướng dẫn sử dụng trên DISTINCT Clause để biết thêm thông tin.

+0

SQL thực hiện điều này vì khái niệm 'DISTINCT' tự nhiên áp dụng cho toàn bộ danh sách lựa chọn; thay thế sẽ là mệnh đề 'GROUP BY'. Chọn chỉ các kết quả riêng biệt từ một cột duy nhất về cơ bản đòi hỏi một tập hợp nếu có bất kỳ cột bổ sung nào trong truy vấn – Matthew

+0

"Bạn phải bao gồm (các) biểu thức riêng biệt như phần ngoài cùng bên trái của mệnh đề ORDER BY của bạn." Đây là lý do tại sao tôi không thể sử dụng DISTINCT ON ... Tôi cần kết quả theo một thứ tự cụ thể được xác định bởi một trường không liên quan đến mệnh đề DISTINCT ON. – mindtonic

+0

+1, ah để postgres có DISTINCT ON ... học tập. cảm ơn. – Unreason

2

Sử dụng truy vấn phụ đã được đề xuất bởi một người nào đó trên kênh #postgresql irc. Nó làm việc:

SELECT user_id 
FROM (SELECT DISTINCT ON (user_id) * FROM creations) ss 
ORDER BY created_at DESC 
LIMIT 20; 
+0

Điều này sẽ vẫn tạo user_id trùng lặp nếu cùng user_id có hai trong số 20 giá trị created_at đầu tiên – Matthew

+0

@mindtonic, mặc dù đây là câu trả lời của Bill, câu trả lời này có thể sai - nếu bạn không sử dụng ORDER BY với DISTINCT ON thì lựa chọn giá trị cho các trường khác là 'triển khai cụ thể' (đọc: rất có thể khớp với thứ tự chèn, nhưng không được bảo đảm, xem tài liệu) – Unreason

+0

@Matthew PK, nó sẽ không - truy vấn phụ sẽ nhận được user_id riêng biệt. Giai đoạn. – Unreason

3

GROUP BY phải đảm bảo giá trị riêng biệt của các cột được nhóm, điều này có thể cung cấp cho bạn những gì bạn đang theo dõi.

(Lưu ý tôi đưa vào 2 cent của tôi mặc dù tôi không quen thuộc với PostgreSQL, mà đúng hơn là MySQL và Oracle)

Trong MySql

SELECT user_id, created_at 
FROM creations 
GROUP BY user_id 
ORDER BY user_id 

Trong Oracle sqlplus

SELECT user_id, FIRST(created_at) 
FROM creations 
GROUP BY user_id 
ORDER BY user_id 

Chúng sẽ cung cấp cho bạn user_id theo sau là trước tiêncreated_at được liên kết với sốđó. Nếu bạn muốn created_at khác nhau, bạn có tùy chọn thay thế FIRST bằng các chức năng khác như AVG, MIN, MAX hoặc LAST trong Oracle, bạn cũng có thể thử thêm ORDER BY trên các cột khác (bao gồm cả các cột không được trả lại) created_at.

3

Câu hỏi của bạn không được xác định rõ ràng - khi bạn nói bạn cũng cần dữ liệu khác từ cùng một hàng bạn không xác định hàng nào.

Bạn nói rằng bạn cần phải đặt kết quả theo created_at, vì vậy tôi sẽ giả mà bạn muốn giá trị từ hàng với min created_at (sớm nhất).

Điều này bây giờ trở thành một trong những câu hỏi SQL phổ biến nhất - truy xuất các hàng chứa một số giá trị tổng hợp (MIN, MAX).

Ví dụ

SELECT user_id, MIN(created_at) AS created_at 
FROM creations 
GROUP BY user_id 
ORDER BY MIN(create_at) 
LIMIT 20 

Cách tiếp cận này sẽ không cho phép bạn (dễ dàng) chọn giá trị khác từ cùng hàng.

Một cách tiếp cận này sẽ cho phép bạn chọn các giá trị khác là

SELECT c.user_id, c.created_at, c.other_columns 
FROM creations c LEFT JOIN creation c_help 
    ON c.user_id = c_help.user_id AND c.created_at > c_help.create_at 
WHERE c_help IS NULL 
ORDER BY c.created_at 
LIMIT 20 
+0

+1 Bingo. Anh ta thiếu điểm tập hợp. Nếu không có tổng hợp thì không có lý do gì để có một 'DISTINCT' trên bất kỳ cột nào nhỏ hơn toàn bộ tập kết quả. – Matthew

+0

Ok, hãy nói rằng bảng sáng tạo có các trường sau: 'id, user_id, created_at, foo, bar, long_description'. Điều tôi muốn là kéo 20 tác phẩm gần đây nhất 'ORDER BY created_at DESC', nhưng lọc kết quả theo người dùng để chỉ có thể tạo một người dùng cho mỗi người dùng trong tập kết quả. Tôi cũng muốn mang theo các lĩnh vực khác với tôi như 'foo' và' bar' nhưng bỏ qua 'long_descrpition'. Câu trả lời có thực sự tham gia một sáng tạo cho chính nó không? – mindtonic

+0

@mindtonic, đó là câu trả lời, có thể không phải là câu trả lời cho bạn. Ngoài ra, bạn nên thử nó và kiểm tra nó. Có những cách tiếp cận khác: rõ ràng là DISTINCT ON hoạt động (với một chút sắp xếp lại), truy vấn con tương quan cũng sẽ hoạt động, v.v ... – Unreason

3

Nếu bạn muốn created_at gần đây nhất cho mỗi người dùng sau đó tôi đề nghị bạn tổng hợp như thế này:

SELECT user_id, MAX(created_at) 
FROM creations 
WHERE .... 
GROUP BY user_id 
ORDER BY created_at DESC 

này sẽ trả lại created_at gần đây nhất cho mỗi user_id Nếu bạn chỉ muốn 20 vị trí hàng đầu, sau đó nối thêm

LIMIT 20 

EDIT: Điều này về cơ bản là điều tương tự Unreason đã nói ở trên ... xác định từ hàng bạn muốn dữ liệu bằng cách tập hợp.

Các vấn đề liên quan