7.2. Expressões de tabela #

7.2.1. A cláusula FROM
7.2.2. A cláusula WHERE
7.2.3. As cláusulas GROUP BY e HAVING
7.2.4. GROUPING SETS, CUBE, e ROLLUP
7.2.5. Processamento de função de janela

Uma expressão de tabela calcula uma tabela. A expressão de tabela contém uma cláusula FROM, opcionalmente seguida pelas cláusulas WHERE, GROUP BY e HAVING. Expressões de tabela triviais apenas se referem a uma tabela no disco, a chamada tabela base, mas expressões mais complexas podem ser usadas para modificar ou combinar tabelas-base de várias maneiras.

As cláusulas opcionais WHERE, GROUP BY e HAVING na expressão de tabela especificam uma sequência (pipeline) de transformações sucessivas realizadas na tabela derivada da cláusula FROM. Todas estas transformações produzem a tabela virtual que fornece as linhas passadas para a lista de seleção para computar as linhas de saída da consulta.

7.2.1. A cláusula FROM #

A cláusula FROM deriva uma tabela a partir de uma ou mais outras tabelas fornecidas em uma lista de referência de tabela separada por vírgula.

FROM referência_de_tabela [, referência_de_tabela [, ...]]

Uma referência de tabela pode ser um nome de tabela (possivelmente qualificado pelo esquema) ou uma tabela derivada, como uma subconsulta, uma construção JOIN, ou combinações complexas desses. Se mais de uma referência de tabela estiver listada na cláusula FROM, as tabelas são unidas (ou seja, o produto cartesiano de suas linhas é formado; veja abaixo). O resultado da lista do FROM é uma tabela virtual intermediária, que pode estar sujeita a transformações por WHERE, GROUP BY e HAVING, sendo finalmente o resultado da expressão geral de tabela.

Quando uma referência de tabela designa uma tabela que é mãe de uma hierarquia de herança de tabela, a referência de tabela produz linhas não apenas dessa tabela, mas de todas as suas tabelas descendentes, a menos que a palavra-chave ONLY preceda o nome da tabela. Entretanto, a referência produz apenas as colunas que aparecem na tabela designada — quaisquer colunas acrescentadas às sub-tabelas são ignoradas.

Em vez de escrever ONLY antes do nome da tabela, pode ser escrito * depois do nome da tabela para especificar, explicitamente, que as tabelas descendentes devem ser incluídas. Não há mais motivo real para usar esta sintaxe, porque procurar nas tabelas descendentes agora é o comportamento padrão. No entanto, é aceito para manter a compatibilidade com versões mais antigas.

7.2.1.1. Tabelas juntadas #

Uma tabela juntada é uma tabela derivada de duas outras tabelas (reais ou derivadas), segundo as regras específicas do tipo de junção. Estão disponíveis junções internas, externas e cruzadas. A sintaxe geral de uma tabela juntada é:

T1 tipo_de_junção T2 [ condição_de_junção ]

Junções de todos os tipos podem ser encadeadas ou aninhadas: uma, ou ambas, T1 e T2 podem ser tabelas juntadas. Podem ser usados parênteses em torno das cláusulas JOIN para controlar a ordem de junção. Na ausência de parênteses, as cláusulas JOIN aninham da esquerda para a direita.

Tipos de junção

Junção cruzada
T1 CROSS JOIN T2

Para toda combinação possível de linhas de T1 e T2 (ou seja, o produto cartesiano), a tabela juntada conterá uma linha que consiste em todas as colunas de T1 seguida por todas as colunas de T2. Se as tabelas tiverem N e M linhas respectivamente, a tabela juntada terá N * M linhas.

FROM T1 CROSS JOIN T2 equivale a FROM T1 INNER JOIN T2 ON TRUE (veja abaixo). Também equivale a FROM T1, T2.

Nota

Esta última equivalência não se mantém exatamente assim quando estão presentes mais de duas tabelas, porque JOIN liga mais firmemente do que a vírgula. Por exemplo FROM T1 CROSS JOIN T2 INNER JOIN T3 ON condição, não é a igual a FROM T1, T2 INNER JOIN T3 ON condição, porque a condição pode se referir à T1 no primeiro caso, mas não no segundo.

Junções qualificadas
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 ON expressão_booleana
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 USING ( lista_de_colunas_de_junção )
T1 NATURAL { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2

As palavras INNER e OUTER são opcionais em todas as formas. INNER é o padrão; LEFT, RIGHT e FULL implicam em junção externa.

A condição de junção é especificada na cláusula ON ou USING, ou implicitamente pela palavra NATURAL. A condição de junção determina quais linhas das duas tabelas de origem são consideradas correspondentes, conforme explicado em detalhes abaixo.

Os tipos possíveis de junção qualificada são:

INNER JOIN

Para cada linha R1 de T1, a tabela juntada tem uma linha para cada linha em T2 que satisfaz a condição de junção com R1.

LEFT OUTER JOIN

Primeiro, a junção interna é executada. Então, para cada linha em T1 que não satisfaça a condição de junção com nenhuma linha em T2, uma linha juntada é adicionada com valores nulos nas colunas de T2. Assim, a tabela unida sempre tem pelo menos uma linha para cada linha em T1.

RIGHT OUTER JOIN

Primeiro, a junção interna é executada. Então, para cada linha em T2 que não satisfaça a condição de junção com nenhuma linha em T1, uma linha juntada é adicionada com valores nulos nas colunas de T1. Este é o inverso da junção esquerda: a tabela de resultados sempre terá uma linha para cada linha em T2.

FULL OUTER JOIN

Primeiro, a junção interna é executada. Então, para cada linha em T1 que não satisfaça a condição de junção com nenhuma linha em T2, uma linha juntada é adicionada com valores nulos nas colunas de T2. Além disso, para cada linha de T2 que não satisfaça a condição de junção com qualquer linha em T1, uma linha juntada com valores nulos nas colunas de T1 é adicionada.

A cláusula ON é o tipo mais geral de condição de junção: ela recebe uma expressão de valor booleano do mesmo tipo usado em uma cláusula WHERE. Um par de linhas de T1 e T2 corresponde se a expressão ON for avaliada como verdadeira.

A cláusula USING é uma forma abreviada que permite aproveitar a situação específica onde os dois lados da junção usam o mesmo nome para a(s) coluna(s) da junção. Ela recebe uma lista separada por vírgulas dos nomes das colunas compartilhadas e forma uma condição de junção que inclui uma comparação de igualdade para cada uma delas. Por exemplo, juntar T1 e T2 com USING (a, b) produz a condição de junção ON T1.a = T2.a AND T1.b = T2.b.

Além disso, a saída de JOIN USING suprime colunas redundantes: não há necessidade de exibir as duas colunas correspondentes, porque elas devem ter valores iguais. Enquanto JOIN ON produz todas as colunas de T1 seguidas por todas as colunas de T2, JOIN USING produz uma coluna de saída para cada um dos pares de colunas listados (na ordem listada), seguido por quaisquer colunas restantes de T1, seguidas por quaisquer colunas restantes de T2.

Por fim, NATURAL é uma forma abreviada de USING: forma uma lista USING consistindo em todos os nomes de coluna que aparecem igualmente nas duas tabelas de entrada. Assim como em USING, estas colunas aparecem apenas uma vez na tabela de saída. Se não houver nomes de coluna em comun, NATURAL JOIN se comporta como CROSS JOIN.

Nota

USING é razoavelmente seguro com relação a alterações de coluna nas relações juntadas, porque apenas as colunas listadas são combinadas. NATURAL é consideravelmente mais arriscado, porque qualquer alteração de esquema em qualquer relação que cause a presença de um novo nome de coluna correspondente fará com que a junção combine esta nova coluna também.

Para reunir tudo isto, supondo que temos as tabelas t1:

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

e t2:

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

então obteremos os seguintes resultados para as várias junções:

=> 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 linhas)

=> SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num;

 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   3 | c    |   3 | yyy
(2 linhas)

=> SELECT * FROM t1 INNER JOIN t2 USING (num);

 num | name | value
-----+------+-------
   1 | a    | xxx
   3 | c    | yyy
(2 linhas)

=> SELECT * FROM t1 NATURAL INNER JOIN t2;

 num | name | value
-----+------+-------
   1 | a    | xxx
   3 | c    | yyy
(2 linhas)

=> 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 linhas)

=> SELECT * FROM t1 LEFT JOIN t2 USING (num);

 num | name | value
-----+------+-------
   1 | a    | xxx
   2 | b    |
   3 | c    | yyy
(3 linhas)

=> 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 linhas)

=> 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 linhas)

A condição de junção especificada com ON também pode conter condições que não se relacionam diretamente com a junção. Isto pode ser útil para algumas consultas, mas precisa ser pensado com cuidado. Por exemplo:

=> 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 linhas)

Repare que colocar a restrição na cláusula WHERE produz um resultado diferente:

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value = 'xxx';

 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
(1 linha)

Isto acontece, porque uma restrição colocada na cláusula ON é processada antes da junção, enquanto uma restrição colocada na cláusula WHERE é processada depois da junção. Isto não faz diferença com junções internas, mas faz muita diferença com junções externas.

7.2.1.2. Aliases de tabela e de coluna #

Tabelas e referências complexas de tabela podem receber um nome temporário, que é então usado para referenciar a tabela derivada no resto da consulta. Isto é chamado de alias de tabela (pseudônimo).

Para criar um alias de tabela se escreve

FROM referência_de_tabela AS alias

ou

FROM referência_de_tabela alias

A palavra-chave AS é um ruído opcional. alias pode ser qualquer identificador.

Um uso típico de aliases de tabela é para atribuir identificadores curtos a nomes de tabela longos para manter as cláusulas de junção legíveis. Por exemplo:

SELECT * FROM um_nome_de_tabela_muito_longo u
         JOIN outro_nome_de_tabela_muito_longo o ON u.id = o.num;

O alias se torna o novo nome de referência da tabela no que diz respeito à consulta corrente — não é mais permitido se referir à tabela pelo nome original em outro lugar da consulta. Portanto, isto não é válido:

SELECT * FROM minha_tabela AS m WHERE minha_tabela.a > 5;    -- errado

Os aliases de tabela são usados principalmente para conveniência notacional, sendo necessário usá-los para juntar uma tabela com ela mesma (autorreferência), por exemplo:

SELECT * FROM pessoas AS pais JOIN pessoas AS filhos ON pais.id = filhos.pais_id;

São usados parênteses para resolver ambiguidades. No exemplo a seguir, a primeira declaração atribui o alias b para a segunda instância de minha_tabela, mas a segunda declaração atribui o alias ao resultado da junção:

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

Uma outra forma de alias de tabela fornece nomes temporários às colunas da tabela, assim como à própria tabela:

FROM referência_de_tabela [AS] alias ( coluna1 [, coluna2 [, ...]] )

Se forem especificados menos aliases de coluna do que a tabela real possui colunas, as colunas restantes não são renomeadas. Esta sintaxe é notadamente útil para auto-junções ou subconsultas.

Quando um alias é aplicado à saída da cláusula JOIN, o alias esconde os nomes originais dentro da cláusula JOIN. Por exemplo:

SELECT a.* FROM minha_tabela AS a JOIN sua_tabela AS b ON ...

é um comando SQL válido, mas

SELECT a.* FROM (minha_tabela AS a JOIN sua_tabela AS b ON ...) AS c

não é válido; o alias de tabela a não é visível fora do alias c.

7.2.1.3. Subconsultas #

Subconsultas que especificam uma tabela derivada devem ser colocadas entre parênteses. Podem ser atribuídos a elas um nome de alias de tabela e, opcionalmente, nomes de alias de coluna. (como na Seção 7.2.1.2). Por exemplo:

FROM (SELECT * FROM tabela1) AS nome_alias

Este exemplo equivale a FROM tabela1 AS nome_alias. Casos mais interessantes, que não podem ser reduzidos a uma união direta, surgem quando a subconsulta envolve agrupamento ou agregação.

Uma subconsulta também pode ser uma lista VALUES:

FROM (VALUES ('anne', 'smith'), ('bob', 'jones'), ('joe', 'blow'))
     AS nomes(nome, sobrenome)

Novamente, o alias da tabela é opcional. Atribuir nomes de alias às colunas da lista VALUES é opcional, mas é uma boa prática. Para obter mais informações veja a Seção 7.7.

De acordo com o padrão SQL, deve ser fornecido um nome de alias de tabela para uma subconsulta. O PostgreSQL permite ser colocado AS e o alias a ser omitido, mas escrever um alias é uma boa prática em código SQL que pode s er portado para outro sistema.

7.2.1.4. Funções de tabela #

As funções de tabela são funções que produzem um conjunto de linhas, compostas por tipos de dados base (tipos escalares), ou tipos de dado compostos (linhas de tabela). São usadas como tabela, visão ou subconsulta na cláusula FROM da consulta. As colunas retornadas por funções de tabela podem ser incluídas nas cláusulas SELECT, JOIN, ou WHERE, da mesma maneira que as colunas de uma tabela, visão, ou subconsulta.

As funções de tabela também podem ser combinadas usando a sintaxe ROWS FROM, com os resultados retornados em colunas paralelas; neste caso, o número de linhas do resultado é o do maior resultado da função, com os resultados menores completados com valores nulos para ficarem correspondendo.

chamada_de_função ↵
    [WITH ORDINALITY] [[AS] alias_de_tabela [(alias_de_coluna [, ... ])]]
ROWS FROM( chamada_de_função [, ... ] ) ↵
    [WITH ORDINALITY] [[AS] alias_de_tabela [(alias_de_coluna [, ... ])]]

Se for especificada a cláusula WITH ORDINALITY, uma coluna adicional do tipo bigint será adicionada às colunas de resultado da função. Esta coluna numera as linhas do conjunto de resultado da função a partir de 1. (Esta é uma generalização da sintaxe padrão do SQL para UNNEST ... WITH ORDINALITY.) Por padrão, a coluna ordinal é chamada de ordinality, mas um nome diferente de coluna pode ser atribuído a ela usando a cláusula AS.

A função de tabela especial UNNEST pode ser chamada com qualquer número de parâmetros de matriz (array), e retorna um número correspondente de colunas, como se UNNEST (veja a Seção 9.19) tivesse sido chamada em cada parâmetro separadamente, e combinada usando a construção ROWS FROM.

UNNEST( expressão_de_matriz [, ... ] ) ↵
    [WITH ORDINALITY] [[AS] alias_de_tabela [(alias_de_coluna [, ... ])]]

Se o alias_de_tabela não for especificado, o nome da função é usado como nome da tabela; no caso da construção ROWS FROM(), é usado o nome da primeira função.

Se não forem fornecidos aliases de colunas para uma função que retorna um tipo de dados base, então o nome da coluna também é o mesmo nome da função. Para uma função que retorna um tipo composto, as colunas de resultado recebem os nomes dos atributos individuais do tipo.

Alguns exemplos:

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;

Em alguns casos é útil definir funções de tabela que podem retornar diferentes conjuntos de colunas, dependendo de como elas são chamadas. Para permitir isto, a função da tabela pode ser declarada como retornando o pseudo-tipo record sem parâmetros OUT. Quando esta função é usada em uma consulta, a estrutura da linha esperada deve ser especificada na própria consulta, para que o sistema possa saber como analisar e planejar a consulta. Esta sintaxe parece:

chamada_de_função [AS] alias (definição_de_coluna [, ... ])
chamada_de_função AS [alias] (definição_de_coluna [, ... ])
ROWS FROM( ... chamada_de_função AS (definição_de_coluna [, ... ]) [, ... ] )

Quando não é usada a sintaxe ROWS FROM(), a lista de definição_de_coluna substitui a lista de aliases de coluna que poderia ser anexada à cláusula FROM; os nomes nas definições de coluna servem como aliases de coluna. Ao se usar a sintaxe ROWS FROM(), a lista de definição_de_coluna pode ser anexada a cada função membro em separado; ou, se houver apenas uma função membro e nenhuma cláusula WITH ORDINALITY, a lista de definição_de_coluna pode ser escrita no lugar da lista de aliases de coluna seguindo ROWS FROM().

Considere o seguinte exemplo:

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

A função dblink (parte do módulo dblink) executa uma consulta remota. É declarada retornando record, já que pode ser usada para qualquer tipo de consulta. O conjunto real de colunas deve ser especificado na consulta de chamada para que o analisador saiba, por exemplo, como expandir o *.

Este exemplo usa ROWS FROM:

SELECT *
FROM ROWS FROM
    (
        json_to_recordset('[{"a":40,"b":"foo"},{"a":"100","b":"bar"}]')
            AS (a INTEGER, b TEXT),
        generate_series(1, 3)
    ) AS x (p, q, s)
ORDER BY p;

  p  |  q  | s
-----+-----+---
  40 | foo | 1
 100 | bar | 2
     |     | 3
(3 linhas)

São unidas duas funções em um único FROM alvo. json_to_recordset() é instruído a retornar duas colunas, a primeira do tipo integer e a segunda do tipo text. O resultado de generate_series() é usado diretamente. A cláusula ORDER BY ordena os valores das colunas como números inteiros.

7.2.1.5. Subconsultas com LATERAL #

As subconsultas que aparecem na cláusula FROM podem ser precedidas pela palavra-chave LATERAL. Isto lhes permite fazer referência a colunas fornecidas por itens anteriores na cláusula FROM. (Sem LATERAL, cada subconsulta é avaliada independentemente, portanto não pode fazer referência cruzada a qualquer outro item da cláusula FROM).

As funções de tabela que aparecem na cláusula FROM também podem ser precedidas pela palavra-chave LATERAL, mas para as funções a palavra-chave é opcional; os argumentos da função podem conter referências a colunas fornecidas pelos itens anteriores da cláusula FROM em qualquer caso.

O elemento LATERAL pode aparecer no nível superior na lista da cláusula FROM, ou dentro de uma árvore JOIN. Neste último caso, também pode se referir a qualquer elemento que esteja à esquerda do JOIN enquanto posicionado à sua direita.

Quando um elemento da cláusula FROM contém referências cruzadas LATERAL, a avaliação procede da seguinte maneira: para cada linha de um elemento do FROM fornecendo a(s) coluna(s) de referência cruzada(s), ou para o conjunto de linhas de vários elementos do FROM fornecendo as colunas, o elemento LATERAL é avaliado usando os valores das colunas dessa linha ou do conjunto de linhas. A(s) linha(s) resultante(s) é(são) unida(s) como de costume com as linhas a partir das quais foram computadas. Isto é repetido para cada linha ou conjunto de linhas da(s) tabela(s) de origem da(s) coluna(s).

Um exemplo trivial de LATERAL é:

SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) ss;

Isto não é particularmente útil, porque produz exatamente o mesmo resultado da forma mais convencional:

SELECT * FROM foo, bar WHERE bar.id = foo.bar_id;

LATERAL é útil, principalmente, quando a coluna de referência cruzada é necessária para calcular a(s) linha(s) a ser(em) juntadas(s). Um uso comum é fornecer o valor do argumento para uma função que retorna um conjunto de linhas. Por exemplo, supondo que a função vertices(polygon) retorna o conjunto de vértices de um polígono, poderíamos identificar os vértices próximos de um conjunto de polígonos armazenados em uma tabela com:

SELECT p1.id, p2.id, v1, v2
FROM polygons p1, polygons p2,
     LATERAL vertices(p1.poly) v1,
     LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

Esta consulta também pode ser escrita como

SELECT p1.id, p2.id, v1, v2
FROM polygons p1 CROSS JOIN LATERAL vertices(p1.poly) v1,
     polygons p2 CROSS JOIN LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

ou em várias outras formulações equivalentes. (Como já foi mencionado anteriormente, a palavra-chave LATERAL é desnecessária neste exemplo, mas foi usada para maior clareza.)

Muitas vezes é útil usar LEFT JOIN em uma subconsulta LATERAL, para que as linhas de origem apareçam no resultado, mesmo que a subconsulta LATERAL não produza nenhuma linha para elas. Por exemplo, se get_product_names() retorna os nomes dos produtos fabricados por um fabricante, mas alguns fabricantes na tabela não fabricam nenhum produto atualmente, podemos encontrá-los com esta consulta:

SELECT m.name
FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true
WHERE pname IS NULL;

7.2.2. A cláusula WHERE #

A sintaxe da cláusula WHERE é

WHERE condição_de_procura

onde condição_de_procura é qualquer valor de expressão (veja a Seção 4.2) que retorna um valor do tipo boolean.

Após o processamento da cláusula FROM, cada linha da tabela virtual derivada é verificada em relação à condição de procura. Se o resultado da condição for verdadeiro, a linha é mantida na tabela de saída, caso contrário (ou seja, se o resultado for falso ou nulo) ela é descartada. A condição de procura normalmente se refere a pelo menos uma coluna da tabela gerada na cláusula FROM; isto não é necessário, mas caso contrário a cláusula WHERE será bem inútil.

Nota

A condição de junção de uma junção interna pode ser escrita na cláusula WHERE ou na cláusula JOIN. Por exemplo, estas expressões de tabela são equivalentes:

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

ou talvez mesmo

FROM a NATURAL JOIN b WHERE b.val > 5

Qual será usado é principalmente uma questão de estilo. A sintaxe da cláusula JOIN na cláusula FROM provavelmente não é tão portável para outros sistemas de gerenciamento de banco de dados SQL, mesmo que esteja no padrão SQL. Para junções externas não há escolha: elas devem ser feitas na cláusula FROM. A cláusula ON ou USING de uma junção externa não equivale a uma condição WHERE, porque resulta na adição de linhas (para linhas de entrada sem correspondência), assim como na remoção de linhas no resultado final.

Aqui estão alguns exemplos de cláusula 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 é a tabela derivada na cláusula FROM. As linhas que não atendem à condição de procura da cláusula WHERE são eliminadas de fdt. Repare no uso de subconsultas escalares como expressões de valor. Assim como qualquer outra consulta, as subconsultas podem empregar expressões complexas de tabela. Repare, também, como fdt é referenciado nas subconsultas. Qualificar c1 como fdt.c1 só é necessário se c1 também for o nome de uma coluna na tabela de entrada derivada da subconsulta. Mas qualificar o nome da coluna acrescenta clareza, mesmo quando não é necessário. Este exemplo mostra como o escopo do nome da coluna de uma consulta externa se estende a suas consultas internas.

7.2.3. As cláusulas GROUP BY e HAVING #

Após passar pelo filtro WHERE, a tabela de entrada derivada pode estar sujeita a agrupamento, usando a cláusula GROUP BY, e eliminação de linhas de grupo usando a cláusula HAVING.

SELECT lista_de_seleção
    FROM ...
    [WHERE ...]
    GROUP BY referência_de_coluna_de_agrupamento [, referência_de_coluna_de_agrupamento]...

A cláusula GROUP BY é usada para agrupar aquelas linhas da tabela que têm os mesmos valores em todas as colunas listadas. A ordem pela qual as colunas são listadas não importa. O efeito é combinar cada conjunto de linhas com valores comuns em uma linha de grupo representando todas as linhas do grupo. Isto é feito para eliminar a redundância na saída e/ou computar agregações que se aplicam a esses grupos. Por exemplo:

=> SELECT * FROM test1;

 x | y
---+---
 a | 3
 c | 2
 b | 5
 a | 1
(4 linhas)

=> SELECT x FROM test1 GROUP BY x;

 x
---
 a
 b
 c
(3 linhas)

Na segunda consulta, não poderíamos ter escrito SELECT * FROM test1 GROUP BY x, por não haver um valor único para a coluna y que possa ser associado a todos os outros grupos. As colunas de agrupamento podem ser referenciadas na lista de seleção, porque possuem um valor único em cada grupo.

Em geral, se uma tabela for agrupada, as colunas que não estiverem listadas na cláusula GROUP BY não poderão ser referenciadas, exceto em expressões de agregação. Um exemplo com expressão de agregação é:

=> SELECT x, sum(y) FROM test1 GROUP BY x;

 x | sum
---+-----
 a |   4
 b |   5
 c |   2
(3 linhas)

Aqui sum é uma função de agregação que calcula um único valor para todo o grupo. Mais informações sobre as funções de agregação disponíveis podem ser encontradas na Seção 9.21.

Dica

O agrupamento sem expressão de agregação calcula de fato o conjunto de valores distintos em uma coluna. Isto também pode ser obtido usando a cláusula DISTINCT (veja a Seção 7.3.3).

Aqui está outro exemplo: é calculado o total de vendas de cada produto (em vez do total de vendas de todos os produtos):

SELECT id_produto, p.nome, (sum(s.unidades) * p.preco) AS vendas
    FROM produtos p LEFT JOIN sales s USING (id_produto)
    GROUP BY id_produto, p.nome, p.preco;

Nesse exemplo, as colunas id_produto, p.nome e p.preco devem estar na cláusula GROUP BY, uma vez que estão referenciadas na lista de seleção da consulta (mas veja abaixo). A coluna s.unidades não precisa estar na lista GROUP BY, uma vez que é utilizada apenas em uma expressão de agregação (sum(...)), representando a venda de um produto. Para cada produto, a consulta retorna uma linha de resumo sobre todas as vendas do produto.

Se a tabela de produtos estiver configurada de modo que, digamos, id_produto seja a chave primária, então seria suficiente agrupar por id_produto no exemplo acima, uma vez que o nome e o preço seriam funcionalmente dependentes da identificação do produto, e assim não haveria ambiguidade sobre qual nome e valor de preço retornar para cada grupo de identificação do produto.

No SQL estrito, GROUP BY só pode agrupar por colunas da tabela de origem, mas o PostgreSQL estende esta possibilidade permitindo que GROUP BY também agrupe colunas da lista de seleção. O agrupamento por expressões de valor, em vez de apenas nomes de colunas, também é permitido.

Se uma tabela foi agrupada usando GROUP BY, mas somente certos grupos são de interesse, pode ser usada a cláusula HAVING, muito parecida com uma cláusula WHERE, para eliminar grupos do resultado. A sintaxe é:

SELECT lista_de_seleção FROM ... [WHERE ...] GROUP BY ... HAVING expressão_booleana

As expressões na cláusula HAVING podem se referir tanto a expressões agrupadas quanto a expressões não agrupadas (que necessariamente envolvem uma função de agregação).

Exemplo:

=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3;

 x | sum
---+-----
 a |   4
 b |   5
(2 linhas)

=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c';

 x | sum
---+-----
 a |   4
 b |   5
(2 linhas)

Novamente, um exemplo mais realista:

SELECT id_produto, p.nome, (sum(s.unidades) * (p.preco - p.custo)) AS lucro
    FROM produtos p LEFT JOIN vendas v USING (id_produto)
    WHERE v.data > CURRENT_DATE - INTERVAL '4 weeks'
    GROUP BY id_produto, p.nome, p.preco, p.custo
    HAVING sum(p.preco * v.unidades) > 5000;

No exemplo acima, a cláusula WHERE está selecionando as linhas por uma coluna que não está agrupada (a expressão é verdadeira apenas para as vendas ocorridas durante as últimas quatro semanas), enquanto a cláusula HAVING restringe a saída a grupos com vendas brutas totais acima de 5.000. Repare que as expressões de agregação não precisam ser necessariamente as mesmas em todas as partes da consulta.

Se uma consulta tiver chamadas para função de agregação, mas não tiver nenhuma cláusula GROUP BY, o agrupamento ainda ocorre: o resultado será uma única linha de grupo (ou, talvez, nenhuma linha, se a única linha for eliminada pela cláusula HAVING). O mesmo é verdadeiro se tiver uma cláusula HAVING, mesmo sem nenhuma chamada de função de agregação ou cláusula GROUP BY.

7.2.4. GROUPING SETS, CUBE, e ROLLUP #

Operações de agrupamento mais complexas do que as descritas acima são possíveis usando o conceito de conjuntos de agrupamento. Os dados selecionados pelas cláusulas FROM e WHERE são agrupados em separado por cada conjunto de agrupamento especificado, agregações computadas para cada grupo, assim como para as cláusulas GROUP BY simples e, então, os resultados são retornados. Por exemplo:

=> SELECT * FROM itens_vendidos;

 marca | tamanho | vendas
-------+---------+--------
 Foo   | L       |     10
 Foo   | M       |     20
 Bar   | M       |     15
 Bar   | L       |      5
(4 linhas)

=> SELECT marca, tamanho, sum(vendas)
   FROM itens_vendidos
   GROUP BY GROUPING SETS ((marca), (tamanho), ());

 marca | tamanho | sum
-------+---------+-----
       |         |  50
 Foo   |         |  30
 Bar   |         |  20
       | L       |  15
       | M       |  35
(5 linhas)

Cada sub-lista de GROUPING SETS pode especificar zero ou mais colunas ou expressões, sendo interpretada da mesma forma como se estivesse diretamente na cláusula GROUP BY. Um conjunto de agrupamento vazio significa que todas as linhas estão agregadas a um único grupo (que é a saída, mesmo que nenhuma linha de entrada esteja presente), como descrito acima para o caso de funções de agregação sem a cláusula GROUP BY.

As referências às colunas ou expressões de agrupamento são substituídas por valores nulos nas linhas de resultado para conjuntos de agrupamento nos quais estas colunas não aparecem. Para distinguir de qual agrupamento resultou uma determinada linha de saída, veja a Tabela 9.66.

Uma notação abreviada é fornecida para especificar dois tipos comuns de conjunto de agrupamento. Uma cláusula com a forma

ROLLUP ( e1, e2, e3, ... )

representa a lista de expressões fornecida e todos os prefixos da lista, incluindo a lista vazia; assim equivale a

GROUPING SETS (
    ( e1, e2, e3, ... ),
    ...
    ( e1, e2 ),
    ( e1 ),
    ( )
)

Isto é comumente usado para análise de dados hierárquicos; por exemplo, salário total por departamento, divisão e total de toda a empresa.

Uma cláusula com a forma

CUBE ( e1, e2, ... )

representa a lista fornecida e todos os seus subconjuntos possíveis (ou seja, o conjunto de potência). Dessa forma

CUBE ( a, b, c )

equivale a

GROUPING SETS (
    ( a, b, c ),
    ( a, b    ),
    ( a,    c ),
    ( a       ),
    (    b, c ),
    (    b    ),
    (       c ),
    (         )
)

Os elementos individuais de uma cláusula CUBE ou ROLLUP podem ser expressões individuais, ou sub-listas de elementos entre parênteses. Nesse último caso, as sub-listas são tratadas como unidades individualmente para fins de geração dos conjuntos de agrupamentos individuais. Por exemplo:

CUBE ( (a, b), (c, d) )

equivale a

GROUPING SETS (
    ( a, b, c, d ),
    ( a, b       ),
    (       c, d ),
    (            )
)

e

ROLLUP ( a, (b, c), d )

equivale a

GROUPING SETS (
    ( a, b, c, d ),
    ( a, b, c    ),
    ( a          ),
    (            )
)

As construções CUBE e ROLLUP podem ser usadas diretamente na cláusula GROUP BY, ou aninhadas dentro da cláusula GROUPING SETS. Se uma cláusula GROUPING SETS for aninhada dentro de outra, o efeito é o mesmo como se todos os elementos da cláusula interna tivessem sido escritos diretamente na cláusula externa.

Se vários itens de agrupamento forem especificados em uma única cláusula GROUP BY, então a lista final de conjuntos de agrupamento é o produto Cartesiano dos itens individuais. Por exemplo, a lista final de conjuntos de agrupamentos é o produto cruzado dos itens individuais:

GROUP BY a, CUBE (b, c), GROUPING SETS ((d), (e))

equivale a

GROUP BY GROUPING SETS (
    (a, b, c, d), (a, b, c, e),
    (a, b, d),    (a, b, e),
    (a, c, d),    (a, c, e),
    (a, d),       (a, e)
)

Ao especificar vários itens de agrupamento juntos, o conjunto final de conjuntos de agrupamento poderá conter repetições. Por exemplo:

GROUP BY ROLLUP (a, b), ROLLUP (a, c)

equivale a

GROUP BY GROUPING SETS (
    (a, b, c),
    (a, b),
    (a, b),
    (a, c),
    (a),
    (a),
    (a, c),
    (a),
    ()
)

Se estas repetições forem indesejáveis, poderão ser removidas usando a cláusula DISTINCT diretamente no GROUP BY. Portanto:

GROUP BY DISTINCT ROLLUP (a, b), ROLLUP (a, c)

equivale a

GROUP BY GROUPING SETS (
    (a, b, c),
    (a, b),
    (a, c),
    (a),
    ()
)

Isto não é idêntico a usar SELECT DISTINCT, porque as linhas de saída ainda podem conter duplicidades. Se alguma das colunas não agrupadas contiver NULL, será indistinguível do NULL usado quando esta mesma coluna for agrupada.

Nota

A construção (a, b) é normalmente reconhecida em expressões como construtora de linha. Dentro da cláusula GROUP BY, isto não se aplica nos níveis superiores das expressões, e (a, b) é analisada como uma lista de expressões conforme descrito acima. Se por algum motivo for necessário um construtor de linha em uma expressão de agrupamento, deve ser usado ROW(a, b).

7.2.5. Processamento de função de janela #

Se a consulta tiver alguma função de janela, estas funções são avaliadas após qualquer agrupamento, agregação e filtragem HAVING ser realizada. Ou seja, se a consulta usar quaisquer agregações, GROUP BY ou HAVING, então as linhas vistas pelas funções de janela são as linhas do grupo em vez das linhas da tabela original de FROM / WHERE. Veja a Seção 3.5 (Tutorial), a Seção 9.22 (Funções de Janela) e a Seção 4.2.8 (Sintaxe do SQL).

Quando são usadas várias funções de janela, todas as funções de janela que possuem cláusulas PARTITION BY e ORDER BY equivalentes em suas definições de janela têm a garantia de ver a mesma ordem das linhas de entrada, mesmo que a cláusula ORDER BY não determine a ordem de forma única. Entretanto, não há garantias quanto à avaliação de funções que possuam especificações de PARTITION BY ou ORDER BY diferentes. (Nestes casos, geralmente é necessária uma etapa de classificação entre as passagens de avaliações da função de janela, e não há garantia de que a classificação preserve a ordem das linhas que o ORDER BY considera equivalentes.)

No momento, as funções de janela sempre requerem dados pré-ordenados, e assim a saída da consulta será ordenada segundo uma ou outra cláusula das funções de janela PARTITION BY/ORDER BY. Entretanto, não é recomendável confiar nisto. Use uma cláusula explícita de nível superior ORDER BY se quiser ter certeza de que os resultados estão ordenados de uma determinada maneira.