2017-10-04 6 views
2

Ich verstehe, wie DISTINCT funktioniert, aber ich verstehe nicht DISTINCT ON (expression).SQL: Was macht "DISTINCT ON (Ausdruck)"?

Nehmen Sie das erste Beispiel aus diesem Screenshot:

enter image description here

Wie funktioniert der (a % 2) Teil alles beeinflussen? Sagt es, wenn a % 2 als wahr auswertet, dann zurückgeben, dann für alle anderen Tupel weitermachen, aber nur zurückgeben, wenn der zurückgegebene Wert verschieden ist?

Antwort

1

a% 2 ist Moduloperator. Sie können nur 0 oder 1 erhalten (NULL, wenn Spalte nullfähig ist).

Zum Beispiel:

i | a | a%2 
1  10  0 
2  11  1 
3  12  0 
4  13  0 

Code:

CREATE TABLE r(i INT, a INT); 
INSERT INTO r(i, a) VALUES (1,10), (2,11),(3,12),(4,13); 

SELECT DISTINCT ON (a%2) a 
FROM r; 

Ausgang:

10 
11 

SELECT DISTINCT ON (a%2) a 
FROM r 
ORDER BY a%2,i DESC; 

Ausgang:

12 
13 

Rextester Demo

2

Während die vorherige Antwort richtig erscheint, fühle ich nicht, dass es besonders klar ist.

Die Schnipsel aus dem Official documentation für PostGreSQL ist wie folgt ...

DISTINCT ON (expression [...]) hält nur die erste Zeile eines jeden Satzes von Zeilen, in denen die angegebenen Ausdrücke zu bewerten, gleich. [...] Beachten Sie, dass die "erste Zeile" jedes Satzes unvorhersehbar ist, es sei denn, ORDER BY wird verwendet, um sicherzustellen, dass die gewünschte Zeile zuerst angezeigt wird. [...] Die DISTINCT ON-Ausdrücke müssen mit den äußersten ORDER BY-Ausdrücken übereinstimmen.

Der erste Punkt ist, dass alles, was Sie in der ON() setzen, muss zunächst in der der ORDER BY aus Gründen kommen, die hoffentlich in Kürze klar werden wird ...

SELECT DISTINCT ON (a) a, b, c FROM a_table ORDER BY a, b 

Die Ergebnisse dann gefiltert werden, so dass für jede der verschiedenen Entitäten nur die erste Zeile zurückgegeben wird.


Zum Beispiel ...

CREATE TABLE example (
    id    INT, 
    person_id  INT, 
    address_id  INT, 
    effective_date DATE 
); 

INSERT INTO 
    example (id, person_id, address_id, effective_date) 
VALUES 
    (1, 2, 1, '2000-01-01'), -- Moved to first house 
    (5, 2, 2, '2004-08-19'), -- Went to uni 
    (9, 2, 1, '2007-06-12'), -- Moved back home 

    (2, 4, 3, '2007-05-18'), -- Moved to first house 
    (3, 4, 4, '2016-02-09') -- Moved to new house 
; 

SELECT DISTINCT ON (person_id) 
    * 
FROM 
    example 
ORDER BY 
    person_id, 
    effective_date DESC 
; 

Dies wird die Ergebnisse der Reihenfolge, so dass alle Datensätze für jede Person zusammenhängen, von den jüngsten Datensatz zur ältesten bestellt. Dann wird für jede Person der erste Datensatz zurückgegeben. So geben Sie die letzte Adresse für jede Person an.

Step 1 : Apply the ORDER BY... 

id | person_id | address_id | effective_date 
----+-----------+------------+---------------- 
    9 |  2 |  1  | '2007-06-12' 
    5 |  2 |  2  | '2004-08-19' 
    1 |  2 |  1  | '2000-01-01' 
    3 |  4 |  4  | '2016-02-09' 
    2 |  4 |  3  | '2007-05-18' 

Step 2 : filter to just the first row per person_id 

id | person_id | address_id | effective_date 
----+-----------+------------+---------------- 
    9 |  2 |  1  | '2007-06-12' 
    3 |  4 |  4  | '2016-02-09' 


Es ist im Großen und Ganzen entspricht dies folgenden ...

SELECT 
    * 
FROM 
(
    SELECT 
     *, 
     ROW_NUMBER() OVER (PARTITION BY person_id 
           ORDER BY effective_date DESC) AS person_address_ordinal 
    FROM 
     example 
) 
    AS sorted_example 
WHERE 
    person_address_ordinal = 1 


Was die Frage, was (a % 2) tut, es ist nur eine mathematische Berechnung für MOD(a, 2) , so könnten Sie folgendes tun ...

CREATE TABLE example (
    id    INT, 
    score   INT 
); 

INSERT INTO 
    example (id, score) 
VALUES 
    (1, 2), 
    (2, 6), 
    (3, 5), 
    (4, 3), 
    (5, 4), 
; 

SELECT DISTINCT ON (id % 2) 
    * 
FROM 
    example 
ORDER BY 
    id % 2, 
    score DESC 
; 

, die die höchste Punktzahl für die sogar id s geben würde (wo id % 2 gleich 0), dann die höchste Punktzahl der ungeraden id s (wo id % 2 gleich 1).

Step 1 : Apply the ORDER BY... 

id | score 
----+------- 

    2 | 6  -- id % 2 = 0 
    4 | 3  -- id % 2 = 0 

    3 | 5  -- id % 2 = 1 
    5 | 4  -- id % 2 = 1 
    1 | 2  -- id % 2 = 1 

Step 2 : filter to just the first row per `id % 2` 

id | score 
----+------- 
    2 | 6  -- id % 2 = 0 
    3 | 5  -- id % 2 = 1