2013-09-22 16 views
11

zdałem sobie sprawę, że zapytanie do bazy danych wracał nieoczekiwane rezultaty zrobić do mojego niewłaściwego użycia „DISTINCT ON” i „grupa BY”PostgreSQL - „DISTINCT ON” i „GROUP BY” składni

Mam nadzieję, że ktoś może uspokój mnie o tym. Rzeczywista zapytanie jest dość skomplikowane, więc będę głupi, to w dół:

Mam tabeli zapytania/wewnętrzny, który składa się z object_id i datownika:

CREATE TABLE test_select (object_id INT , event_timestamp timestamp); 
COPY test_select (object_id , event_timestamp) FROM stdin (DELIMITER '|'); 
1   | 2013-01-27 21:01:20 
1   | 2012-06-28 14:36:26 
1   | 2013-02-21 04:16:48 
2   | 2012-06-27 19:53:05 
2   | 2013-02-03 17:35:58 
3   | 2012-06-14 20:17:00 
3   | 2013-02-15 19:03:34 
4   | 2012-06-13 13:59:47 
4   | 2013-02-23 06:31:16 
5   | 2012-07-03 01:45:56 
5   | 2012-06-11 21:33:26 
\. 

Próbuję wybrać odrębną ID, sortowane/deduplikowane przez datownika na odwrotnej Chrońmy

więc wyniki powinny być [4, 1, 3, 2, 5]

myślę, że to nie to, czego potrzebuję (wydaje się):

SELECT object_id 
FROM test_select 
GROUP BY object_id 
ORDER BY max(event_timestamp) DESC 
; 

Do celów testowania/audytu czasami chcę dołączyć pole sygnatury czasowej. Nie mogę chyba wymyślić, jak dołączyć inne pole z tym zapytaniem.

Czy ktoś może wskazać na rażące problemy w moim sql powyżej, lub sugestie, w jaki sposób uwzględnić informacje audytu?

Odpowiedz

14

Aby móc wybierz wszystkie kolumny i nie tylko object_id i MAX(event_timestamp), można użyć DISTINCT ON

SELECT DISTINCT ON (object_id) 
    object_id, event_timestamp ---, more columns 
FROM test_select 
ORDER BY object_id, event_timestamp DESC ; 

Jeśli chcesz wyniki zamówionego przez event_timestamp DESC a nie object_id, trzeba umieścić go w tabeli pochodzącej lub CTE:

SELECT * 
FROM 
    (SELECT DISTINCT ON (object_id) 
     object_id, event_timestamp ---, more columns 
    FROM test_select 
    ORDER BY object_id, event_timestamp DESC 
) AS t 
ORDER BY event_timestamp DESC ; 

Alternatywnie, można użyć funkcji okna, jak ROW_NUMBER():

WITH cte AS 
    (SELECT ROW_NUMBER() OVER (PARTITION BY object_id 
           ORDER BY event_timestamp DESC) 
      AS rn, 
      object_id, event_timestamp ---, more columns 
    FROM test_select 
) 
SELECT object_id, event_timestamp ---, more columns 
FROM cte 
WHERE rn = 1 
ORDER BY event_timestamp DESC ; 

lub kruszywo MAX() z OVER:

WITH cte AS 
    (SELECT MAX(event_timestamp) OVER (PARTITION BY object_id) 
      AS max_event_timestamp, 
      object_id, event_timestamp ---, more columns 
    FROM test_select 
) 
SELECT object_id, event_timestamp ---, more columns 
FROM cte 
WHERE event_timestamp = max_event_timestamp 
ORDER BY event_timestamp DESC ; 
+0

Dzięki! Twoim drugim przykładem była składnia, którą próbowałem zrozumieć. Przetestuję inne zapytania pod kątem wydajności później i zobaczę, czy mogę ich użyć. Generuję to zapytanie w sposób generatywny na podstawie danych wprowadzonych przez użytkownika, a ten drugi przykład jest "łatwy do wykonania". –

3

Prawdopodobnie nie jest to najlepszy sposób radzenia sobie z tym, ale można spróbować użyć funkcji okna:

SELECT DISTINCT object_id, MAX(event_timestamp) 
OVER (PARTITION BY object_id) 
FROM test_select ORDER BY max DESC; 

Z drugiej strony to działa tak samo:

SELECT object_id, MAX(event_timestamp) as max_event_timestamp 
FROM test_select 
GROUP BY object_id 
ORDER BY max_event_timestamp DESC;