Documentazione di PostgreSQL 9.0 > Il linguaggio SQL > Query > Expressioni di tabella
PrecedenteQueryElenchi di selezioneSuccessivo

7.2. Expressioni di tabella

Un'espressione di tabella calcola una tabella. L'espressione di tabella contiene una clausola FROM che opzionalmente è seguita dalle clausole da WHERE, GROUP BY e HAVING. Espressioni di tabella insignificanti si riferiscono semplicemente a una tabella su disco, chiamata tabella base, ma espressioni più complesse possono essere usate per modificare o combinare tabelle base in diversi modi.

Le clausole opzionali WHERE, GROUP BY e HAVING nell'espressione di tabella specificano una pipeline di successive trasformazioni eseguite sulla tabella derivata nella clausola FROM Tutte queste trasformazioni producono una tabella virtuale che fornisce le righe passate all'elenco di selezione per ricavare le righe di output della dquery.

7.2.1. La clausola FROM

The FROM Clause derives a La FROM Clause ricava una tabella da uno o più riferimenti di tabella fornite in un elenco separato da virgole.

FROM table_reference [, table_reference [, ...]]

Un riferimento di tabella può essere il nome di una tabella (possibilimente qualificato dallo schema), o una tabella derivata come una sottoquery, una join di tabella, o combinazioni complesse di queste. Se viene elencato più di un riferimento di tabella nella clausola FROM vengono sottoposti a "cross-join" (si veda sotto) per formare la tabella virtuale intermedia che può essere soggetto di trasformazioni da parte di clausole WHERE, GROUP BY, e HAVING e sarà finalmente il risultato dell'espressione di tabella globale.

Quando un riferimento di tabella nomina una tabella genitore di una gerarchia di ereditarietà, il riferimento di tabella produce righe non solo di quella tabella, ma anche di tutte le tabelle discendenti, a meno che la parola chiave ONLY preceda il nome della tabella. Comunque, il riferimento produce solo colonne che appaiono nella tabella nominata - ogni colonna aggiunta nelle sotto-tabelle viene ignorata.

7.2.1.1. Tabelle Join

Una tabella join è una tabella derivata da altre due tabelle (reali o derivate) seguendo le regole del particolare tipi di join. Sono disponibili inner join, outer join e cross join.

Tipi di join

Cross join
T1 CROSS JOIN T2

Per ogni possibile combinazione di righe provenienti da T1 e T2 (per es., un prodotto cartesiano), la tabella risultante conterrà una riga comprendente tutte le colonne di T1 seguite da tutte le colonne di T2. Se le tabelle hanno rispettivamente N e M righe, la tabella risultante avrà N * M righe.

FROM T1 CROSS JOIN T2 è equivalente a FROM T1, T2. È anche equivalente a FROM T1 INNER JOIN T2 ON TRUE (si veda sotto).

Join qualificate
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 ON boolean_expression
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 USING ( join column list )
T1 NATURAL { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2

Le parole INNER e OUTER sono opzionali in tutte le forme. INNER è il predefinito; LEFT, RIGHT, e FULL implicano una outer join.

La condizione di join viene specificata nella clausola ON o USING, o implicitamente dalla parola NATURAL. La condizione di join determina quali righe delle due tabelle sorgente sono considerate «corrispondere», come spiegato in dettaglio sotto.

La clausola ON è il tipo più generale di condizione join: essa prende un espressione di valore booleano dello stesso tipo usato in una clausola WHERE. una coppia di righe di T1 e T2 corrisponde se l'espressione ON risulta true per loro.

USING è una notazione abbreviata: accetta elenchi di nomi di colonna separati da virgole, che le tabelle sottoposte a join devono avere in comune, e forma una condizione di join specificando l'uguaglianza di ognuna di queste coppie di colonne. Inoltre, l'output di JOIN USING ha una colonna per ognuna delle coppie delle colonne di input messe in relazione. Così, USING (a, b, c) è equivalente a ON (t1.a = t2.a AND t1.b = t2.b AND t1.c = t2.c) ad eccezione del fatto che se viene usato ON ci saranno due colonne a, b, e c nel risultato, mentre con USING ce ne sarà solo una per ognuna (e appariranno per prime se viene usato SELECT *).

Infine, NATURAL è una forma abbreviata di USING: essa forma una lista USING consistente esattamente di quei nomi di colonna che compaiono in entrambe le tabelle di ingresso. Come con USING, queste colonne compaiono soltanto una volta nella tabella di output.

I tipi possibili di join qualificate sono:

INNER JOIN

Per ogni riga R1 di T1, la tabella sottoposta a join ha una riga per ogni riga di T2 che soddisfa la condizione di join con R1.

LEFT OUTER JOIN

Prima viene eseguita una inner join. Quindi, per ogni riga in T1 che non soddisfa la condizione di join con qualsiasi riga in T2, una riga sottoposta a join viene aggiunta con valori null nelle colonne di T2. Così, la tabella risultante avrà almeno una riga per ogni riga in T1.

RIGHT OUTER JOIN

Prima viene eseguita una inner join. Quindi, per ogni riga in T2 che non soddisfa la condizione di join con una qualsiasi riga in T1, una riga sottoposta a join viene aggiunta con valori null nelle colonne di T1. Questo è il contrario di una left join: la tabella risultante avrà sempre una riga per ogni riga di T2.

FULL OUTER JOIN

Prima viene eseguita una inner join. Quindi, per ogni riga in T1 che non soddisfa la condizione di join con una qualsiasi riga in T2, una riga sottoposta a join viene aggiunta con valori null nelle colonne di T2. Inoltre, per ogni riga di T2 che non soddisfa la condizione join con un qualsiasi riga in T1, viene aggiunta una riga join con valori null nelle colonne di T1.

Join di tutti i tipi possono essere concatenate o annidate: T1 e/o T2 possono essere tabelle sottoposte a join. Possono essere usate parentesi attorno alle clausole JOIN per controllare l'ordine della join. In assenza di parentesi, clausole JOIN vanno da sinistra a destra.

Per mettere insieme tutto questo, assumiamo di avere le tabelle t1:

 num | name
-----+------
   1 | a
   2 | b
   3 | c

e t2:

 num | value
-----+-------
   1 | xxx
   3 | yyy
   5 | zzz

per i diversi tipi di join, otterremo i seguenti risultati:

=> SELECT * FROM t1 CROSS JOIN t2;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   1 | a    |   3 | yyy
   1 | a    |   5 | zzz
   2 | b    |   1 | xxx
   2 | b    |   3 | yyy
   2 | b    |   5 | zzz
   3 | c    |   1 | xxx
   3 | c    |   3 | yyy
   3 | c    |   5 | zzz
(9 rows)

=> SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   3 | c    |   3 | yyy
(2 rows)

=> SELECT * FROM t1 INNER JOIN t2 USING (num);
 num | name | value
-----+------+-------
   1 | a    | xxx
   3 | c    | yyy
(2 rows)

=> SELECT * FROM t1 NATURAL INNER JOIN t2;
 num | name | value
-----+------+-------
   1 | a    | xxx
   3 | c    | yyy
(2 rows)

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   2 | b    |     |
   3 | c    |   3 | yyy
(3 rows)

=> SELECT * FROM t1 LEFT JOIN t2 USING (num);
 num | name | value
-----+------+-------
   1 | a    | xxx
   2 | b    |
   3 | c    | yyy
(3 rows)

=> SELECT * FROM t1 RIGHT JOIN t2 ON t1.num = t2.num;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   3 | c    |   3 | yyy
     |      |   5 | zzz
(3 rows)

=> SELECT * FROM t1 FULL JOIN t2 ON t1.num = t2.num;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   2 | b    |     |
   3 | c    |   3 | yyy
     |      |   5 | zzz
(4 rows)

La condizione di join specificata con ON può contenere anche condizioni che non si riferiscono direttamente alla join. Questo può tornare utile per alcune query ma deve essere progettato con attenzione. Per esempio:

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num AND t2.value = 'xxx';
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   2 | b    |     |
   3 | c    |     |
(3 rows)

Si noti che posizionare la restrizione nella clausola WHERE produce un risultato diverso:

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value = 'xxx';
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
(1 row)

Questo perchè una restrizione posizionata nella clausola ON viene processata prima della join, mentre una restrizione posta nella clausola WHERE viene processata dopo la join.

7.2.1.2. Alias di tabella e colonna

Un nome temporaneo può essere dato a tabelle e riferimenti complessi di tabella per essere usato come riferimento nel resto della query. Questo prende il nome di alias di tabella

Per creare un alias di tabella, scrivere

FROM table_reference AS alias

o

FROM table_reference alias

La parola chiave AS è superflua. alias può essere qualsiasi identificatore.

Una tipica applicazione degli alias di tabella è quando si vuole assegnare identificatori a nomi di tabella lunghi per mantere leggibili le clausole join. Per esempio:

SELECT * FROM some_very_long_table_name s JOIN another_fairly_long_name a ON s.id = a.num;

L'alias diventa il nuovo nome del riferimento alla tabella per quanto concerne la query corrente - non è permesso fare riferimento alla tabella col nome originale altrove nella query. Per questo, questo non è valido:

SELECT * FROM my_table AS m WHERE my_table.a > 5;    -- sbagliato

Gli alias di tabella sono principalmente comodità di notazione, ma è necessario usarle quando si effettua il join di una tabella con sè stessa, per es.:

SELECT * FROM people AS mother JOIN people AS child ON mother.id = child.mother_id;

In più, è richiesto un alias se il riferimento alla tabella è una sottoquery (si veda Sezione 7.2.1.3, «Sottoquery»).

Per risolvere le ambiguità vengono usate le parentesi. Nell'esempio seguente, la prima istruzione assegna l'alias b alla seconda istanza di my_table, ma la seconda istruzione assegna l'alias al risultato della join:

SELECT * FROM my_table AS a CROSS JOIN my_table AS b ...
SELECT * FROM (my_table AS a CROSS JOIN my_table) AS b ...

Un altra forma di alias per le tabelle fornisce un nome temporaneo alle colonne della tabella, nello stesso modo fatto per le tabelle:

FROM table_reference [AS] alias ( column1 [, column2 [, ...]] )

Se sono specificati alias per un numero di colonne inferiori rispetto a quelle contenute nella tabella, le restanti colonne non verranno rinominate. Questa sintassi è utilizzata specialmente per self-join o sottoquery.

Quando un alias è applicato all'output di una clausula JOIN, utilizzando una di queste forme, l'alias nasconde i nomi originali dentro la JOIN. Per esempio:

SELECT a.* FROM my_table AS a JOIN your_table AS b ON ...

è SQL valido, ma:

SELECT a.* FROM (my_table AS a JOIN your_table AS b ON ...) AS c

non è valido; l'alias di tabella a non è visibile al di fuori dell'alias c.

7.2.1.3. Sottoquery

Le subquery che specificano una tabella derivata devono essere racchiuse tra parentesi e devono essere assegnate ad un nome alias di tabella. (Vedere Sezione 7.2.1.2, «Alias di tabella e colonna»). Per esempio:

FROM (SELECT * FROM table1) AS alias_name

Questo esempio è equivalente a FROM tabella1 AS nome_alias. Casi più interessanti, che non si riducono ad una semplice join, si presentano quando la subquery coinvolge raggruppamenti o aggregazioni.

A subquery can also be a VALUES list:

FROM (VALUES ('anne', 'smith'), ('bob', 'jones'), ('joe', 'blow'))
     AS names(first, last)

Di nuovo, è richiesto un alias di tabella. Assegnare alias alle colonne dell'elenco VALUES è opzionale, ma comunque una buona pratica. Per maggiori informazioni si veda Sezione 7.7, «Elenci VALUES».

7.2.1.4. Funzioni di tabella

Funzioni di tabella sono funzioni che producono un insieme di righe, fatte sia da tipi di dato di base (tipi scalari) che da tipi di dato composti (righe di tabella). Sono usate come tabella, vista, o sottoquery nella clausola FROM di una query. Le colonne restituite dalle funzioni tabella possono essere incluse nelle SELECT, JOIN o nelle clausole WHERE allo stesso modo di una tabella, una vista, o una colonna di sottoquery.

Se una funzione di tabella restituisce un tipo di dato di base, il nome della singola colonna risultante corrisponde al nome della funzione. Se la funzione restituisce un tipo composto, le colonne risultanti avranno gli stessi nomi degli attributi del tipo.

Una funzione di tabella può essere sottoposta ad alias nella clausola FROM, ma può anche essere lasciata senza alias. Se una funzione viene usata nella clausola FROM senza alias, il nome della funzione viene usato come nome della tabella risultante.

Alcuni esempi:

CREATE TABLE foo (fooid int, foosubid int, fooname text);

CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$
    SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;

SELECT * FROM getfoo(1) AS t1;

SELECT * FROM foo
    WHERE foosubid IN (
                        SELECT foosubid
                        FROM getfoo(foo.fooid) z
                        WHERE z.fooid = foo.fooid
                      );

CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);

SELECT * FROM vw_getfoo;

In alcuni casi è utile definire funzioni di tabella che possono restituire insiemi di colonne diversi dipendentemente da come sono invocate. Per realizzare questo, la funzione di tabella può essere dichiarata che ritorni lo pseudotipo record. Quando una funzione di questo tipo viene usata in una query, la struttura di riga aspettata dev'essere specificata nella query stessa, così che il sistema possa sapere come eseguire il parsing e il plan della query. Considerare l'esempio:

SELECT *
    FROM dblink('dbname=mydb', 'SELECT proname, prosrc FROM pg_proc')
      AS t1(proname name, prosrc text)
    WHERE proname LIKE 'bytea%';

La funzione dblink esegue una query remota (di veda contrib/dblink). È dichiarata per restiruire record dato che potrebbe essere usata per qualsiasi tipo di query. L'insieme di colonne deve essere specificato nella query chiamante così che il il parser sappia, per esempio, come * dovrebbe espandersi.

7.2.2. La clausola WHERE

La sintassi della WHERE Clause è

WHERE search_condition

Dove search_condition è qualsiasi espressione di valore (si veda Sezione 4.2, «Espressioni di valore») che restituisce un valore di tipo boolean.

Dopo che è terminata l'elaborazione della clausola FROM , ogni riga della tabella virtuale derivata viene confrontata alla condizione di ricerca. Se il risultato della condizione è true, la riga viene mantenuta nella tabella di output, altrimenti viene scartato (per es., se il risultato è false o null). La condizione di ricerca tipicamente si riferisce almeno ad una colonna della tabella generata nella clausola FROM; questo non è richiesto, ma altrimenti la clausola WHERE sarà abbastanza inutile.

[Nota]

Nota

La condizione di join di una inner join può essere scritta o nella clausola WHERE o nella clausola JOIN. Per esempio, queste espressioni di tabella sono equivalenti:

FROM a, b WHERE a.id = b.id AND b.val > 5

e:

FROM a INNER JOIN b ON (a.id = b.id) WHERE b.val > 5

o forse anche:

FROM a NATURAL JOIN b WHERE b.val > 5

Quale si voglia usare di questi è principalmente una questione di stile. La sintassi JOIN nella clausola FROM probabilmente non è portabile verso altri sistemi di gestione di database SQL, anche se è nello standard SQL. Per outer join non c'è possibilità di scelta: devono essere nella clausola FROM. Le clausole ON o USING di una outer join non è equivalente a una condizione WHERE, perchè corrisponde all'aggiunta di righe (per righe di input non corrispondenti) così come la rimozione di righe nel risultato finale.

Ecco alcuni esempi di clausole WHERE:

SELECT ... FROM fdt WHERE c1 > 5

SELECT ... FROM fdt WHERE c1 IN (1, 2, 3)

SELECT ... FROM fdt WHERE c1 IN (SELECT c1 FROM t2)

SELECT ... FROM fdt WHERE c1 IN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10)

SELECT ... FROM fdt WHERE c1 BETWEEN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10) AND 100

SELECT ... FROM fdt WHERE EXISTS (SELECT c1 FROM t2 WHERE c2 > fdt.c1)

fdt è la tabella derivata nella clausola FROM. Le righe che non soddisfano la condizione di ricerca della clausola WHERE sono eliminate da fdt. Si noti l'uso di sottoquery scalari come valori di espressione. Esattamente come qualsiasi altra query, le sottoquery possono impiegare espressioni di tabella complesse. Notare inoltre come fdt viene referenziato nelle sottoquery. Qualificare c1 come fdt.c1 è necessario solo se c1 è anche il nome di una colonna nella tabella di input derivata della sottoquery. In ogni caso, qualificare il nome della colonna aggiunge chiarezza anche quando non è necessario. Questo esempio mostra come lo scope di nomi di una query outer si estende nelle sue query inner.

7.2.3. Le clausole GROUP BY e HAVING

Dopo che si è passato il filtro WHERE, la tabella di input derivata potrebbe essere soggetta a raggruppamento, usando la clausola GROUP BY, ed eliminazione di righe usando la clausola HAVING.

SELECT select_list
    FROM ...
    [WHERE ...]
    GROUP BY grouping_column_reference [, grouping_column_reference]...

La GROUP BY Clause è usata per raggruppare insieme quelle righe in una tabella che hanno gli stessi valori in tutte le colonne elencate. L'ordine con il quale le colonne sono elencate non è importante. L'effetto è quello di combinare ogni elenco di righe aventi valori in comune in una riga-gruppo che rappresenta tutte le righe del gruppo. Questo viene fatoo per eliminare la ridondanza in output e/o eseguire calcoli aggregati su questi gruppi. Per esempio:

=> SELECT * FROM test1;
 x | y
---+---
 a | 3
 c | 2
 b | 5
 a | 1
(4 rows)

=> SELECT x FROM test1 GROUP BY x;
 x
---
 a
 b
 c
(3 rows)

Nella seconda query, potevamo non aver scritto SELECT * FROM test1 GROUP BY x, dato che non c'è un singolo valore per la colonna y che può essere associato con ogni gruppo. La colonne raggruppate possono essere referenziate nell'elenco select dato che hanno un singolo valore in ogni gruppo.

In generale, se una tabella è raggruppata, colonne che non sono elencate in GROUP BY non possono essere referenziate eccetto in espressioni aggregate. Un esempio con un'espressione aggregata è:

=> SELECT x, sum(y) FROM test1 GROUP BY x;
 x | sum
---+-----
 a |   4
 b |   5
 c |   2
(3 rows)

Qui sum è una funzione aggregata che calcola un valore singolo dell'intero gruppo. Maggiori informazioni sulle funzioni disponibili possono essere trovate in Sezione 9.18, «Funzioni aggregate».

[Suggerimento]

Suggerimento

Ragggruppare senza espressioni aggregate effettivamente calcola l'insieme dei valori distinti in una colonna. Questo può essere ottenuto anche usando la clausola DISTINCT(si veda Sezione 7.3.3, «DISTINCT»).

Ecco un altro esempio: calcola le vendite totali per ogni prodotto (piuttosto che le vendite totali di tutti i prodotti):

SELECT product_id, p.name, (sum(s.units) * p.price) AS sales
    FROM products p LEFT JOIN sales s USING (product_id)
    GROUP BY product_id, p.name, p.price;

In questo esempio, le colonne product_id, p.name e p.price devono essere nella clausola GROUP BY dato che sono referenziate nell'elenco select della query (ma si veda sotto). La colonna s.units non dev'essere nell'elenco GROUP BY dato che è usato solo in un'espressione aggregata (sum(...)), che rappresenta le vendite di un prodotto. Per ogni prodotto, la query restituisce una riga di riassunto su tutte le vendite del prodotto.

Se la tabella dei prodotti è impostat per, diciamo, avere product_id come chiave primaria, allora dovrebbe essere sufficiente raggruppare per product_id nell'esempio sopra, dato che nome e prezzo dovrebbero essere funzionalmente dipendenti. sull'ID di prodotto, e così non ci dovrebbe essere ambiguità su quale valore di nome e prezzo restituire per ogni ID di gruppo di prodotti.

In SQL stretto, GROUP BY può raggruppare solo colonne della tabella sorgente ma PostgreSQL™ lo estende per permettere alla GROUP BY di raggruppare per colonne nell'elenco select. È possibili anche raggruppare per espressioni di valori invece di semplici nomi di colonna.

Se una tabella è stata raggruppata usando GROUP BY, ma interessano solo certi gruppi, può essere usata la clausola HAVING, simile per la maggior parte alla WHERE, per eliminare gruppi dal risultato. La sintassi è:

SELECT select_list FROM ... [WHERE ...] GROUP BY ... HAVING boolean_expression

Espressioni nella clausola HAVING possono riferirsi sia a espressioni raggruppate che a espressioni non raggruppate (che necessariamente coinvolgono una funzione aggregata).

Esempio:

=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3;
 x | sum
---+-----
 a |   4
 b |   5
(2 rows)

=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c';
 x | sum
---+-----
 a |   4
 b |   5
(2 rows)

Di nuovo, un esempio più realistico:

SELECT product_id, p.name, (sum(s.units) * (p.price - p.cost)) AS profit
    FROM products p LEFT JOIN sales s USING (product_id)
    WHERE s.date > CURRENT_DATE - INTERVAL '4 weeks'
    GROUP BY product_id, p.name, p.price, p.cost
    HAVING sum(p.price * s.units) > 5000;

Nell'esempio sopra, la clausola WHERE seleziona righe da una colonna che non è raggruppata (l'esprezzione è true solo per vendite durante le ultime quattro settimane), mentre la clausola HAVING restringe l'output per gruppi con vendite totali maggiori di 5000. Si noti che le espressioni aggregate non hanno necessariamente bisogno di essere le stesse in tutte le parti della query.

Se una query contiene chiamate a funzioni aggregate, ma non clausole GROUP BY, il raggrumento avviene comunque: il risultato è una singola riga-gruppo (o magari nessuna riga, se la riga singola è poi eliminata da HAVING). Lo stesso è true se contiene una clausola HAVING, anche senza nessuna chiamata a funzione aggregata o clausola GROUP BY.

7.2.4. Elaborazione di funzioni window

Se la query contiene funzioni window (si veda Sezione 3.5, «Funzioni di Finestra», Sezione 9.19, «Funzioni window» e Sezione 4.2.8, «Chiamate a funzioni finestra»), queste funzioni sono valutate dopo che qualsiasi raggruppamento, aggregazione, e filtro HAVING venga svolto. Così, se la query usa qualsiasi aggregata, GROUP BY, o HAVING, allora le righe viste dalle funzioni window solo le righe-gruppo invece delle righe originali della tabella provenienti dalla FROM/WHERE.

Quando sono usate molteplici funzioni window, tutte le funzioni window aventi clausole PARTITION BY e ORDER BY sintatticamente equivalenti nelle loro definizioni di windowè garantito siano valutate in un singolo passo sui dati. Perciò vedranno lo stesso ordinamento, anche se la ORDER BY non determina unicamente un ordinamento. Comunque, non sono assicurate garanzie sulla valutazione di funzioni aventi diverse specificazioni di PARTITION BY o ORDER BY. (In qualche caso un certo passo è tipicamente richiesto tra i passi di valutazione di funzioni window, e non è garantita la preservazione di ordinamento di righe che il suo ORDER BY vede come equivalente).

Attualmente, le funzioni window richiedono sempre dati preordinati, e così l'output della query sarà ordinato in accordo all'una o l'altra delle clausole PARTITION BY/ORDER BY delle funzioni window. Non è raccomandabile contare su questo, in ogni caso. Usare una clausola ORDER BY di livello superiore se si vuole essere sicuri che i risultati siano ordinati in modo particolare.

Documentazione di PostgreSQL 9.0 > Il linguaggio SQL > Query > Expressioni di tabella
PrecedenteQueryElenchi di selezioneSuccessivo