PostgreSQL: Consultas SELECT Básicas no PostgreSQL
Última atualização: 2026-08-26
1. O Que Você Vai Aprender
- Usar
SELECTpara consultar colunas, expressões e aliases - Remover linhas duplicadas com
DISTINCT - Filtrar dados com
WHERE - Ordenar com
ORDER BY(incluindo o exclusivoNULLS FIRST/LASTdo PostgreSQL) - Paginar com
LIMIT/OFFSETeFETCH FIRST
2. A História
Bob é um engenheiro de banco de dados em uma plataforma de e-commerce. A plataforma gera cerca de 10.000 pedidos por dia, acumulando 100.000 registros nos últimos 30 dias. A equipe de operações pede a ele para:
- Consultar todos os pedidos dos últimos 30 dias, ordenados por valor do maior para o menor
- Exibir 50 por página, com suporte a paginação
- Filtrar pedidos cancelados
- Remover IDs de clientes duplicados da exibição
Bob precisa de SELECT, WHERE, ORDER BY, LIMIT/OFFSET e DISTINCT para realizar o trabalho.
3. Conceito: Fundamentos do SELECT
(1) Visão Geral da Sintaxe do SELECT
SELECT coluna1, coluna2, ...
FROM nome_tabela
[WHERE condicao]
[ORDER BY coluna [ASC|DESC] [NULLS FIRST|NULLS LAST]]
[LIMIT quantidade [OFFSET inicio]];
SELECT é a instrução SQL mais usada, utilizada para recuperar dados de uma ou mais tabelas.
(2) Todas as Colunas vs Colunas Específicas
| Estilo | Descrição | Desempenho | Recomendado para |
|---|---|---|---|
SELECT * |
Retorna todas as colunas | Pior—transmite dados redundantes | Explorar rapidamente a estrutura da tabela |
SELECT col1, col2 |
Retorna apenas colunas especificadas | Melhor—menos E/S | Consultas em produção |
▶ Exemplo: Consultar Todas as Colunas
SELECT * FROM orders;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
▶ Exemplo: Consultar Colunas Específicas
SELECT order_id, customer_id, total_amount, order_date
FROM orders;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) Aliases de Coluna
Use a palavra-chave AS (opcional) para dar a uma coluna ou expressão um alias, tornando os resultados mais legíveis.
| Uso | Exemplo | Descrição |
|---|---|---|
AS explícito |
price * qty AS subtotal |
Recomendado—claro |
Omitir AS |
price * qty subtotal |
Válido, mas menos legível |
| Alias com aspas | total_amount AS "Order Total" |
Necessário quando o alias tem espaços / maiúsculas |
▶ Exemplo: Usando Aliases de Coluna
SELECT
order_id,
total_amount AS amount,
order_date AS "Order Date",
total_amount * 0.08 AS tax
FROM orders;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(4) Expressões e Colunas Calculadas
SELECT pode fazer mais do que ler colunas—também pode escrever expressões aritméticas, chamadas de função, etc., para produzir colunas calculadas.
▶ Exemplo: Coluna Calculada
SELECT
product_name,
unit_price,
unit_price * 1.1 AS price_with_tax,
ROUND(unit_price * 1.1, 2) AS rounded_price
FROM products;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
4. Conceito: Desduplicação com DISTINCT
(1) Desduplicação Básica
DISTINCT remove linhas duplicadas do conjunto de resultados, comumente usado para contar valores únicos.
▶ Exemplo: Consultar IDs de Clientes Distintos
SELECT DISTINCT customer_id
FROM orders;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) Desduplicação com Múltiplas Colunas
DISTINCT aplica-se à combinação de todas as colunas especificadas, não a uma única coluna.
| Instrução | Escopo da desduplicação |
|---|---|
SELECT DISTINCT a |
Desduplica por a |
SELECT DISTINCT a, b |
Desduplica pela combinação (a, b) |
SELECT DISTINCT ON (a) a, b |
Exclusivo do PostgreSQL: desduplica por a, mantém a primeira linha por grupo |
▶ Exemplo: Desduplicação com Combinação de Múltiplas Colunas
SELECT DISTINCT customer_id, order_status
FROM orders;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) DISTINCT ON: Sintaxe Exclusiva do PostgreSQL
DISTINCT ON (expr) desduplica por uma determinada expressão e retorna a primeira linha de cada grupo (combine com ORDER BY para decidir qual linha).
▶ Exemplo: Último Pedido de Cada Cliente
SELECT DISTINCT ON (customer_id)
customer_id, order_id, order_date, total_amount
FROM orders
ORDER BY customer_id, order_date DESC;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
5. Conceito: Filtragem com WHERE
(1) Operadores de Comparação Básicos
| Operador | Significado | Exemplo |
|---|---|---|
= |
Igual | order_status = 'completed' |
<> ou != |
Diferente | total_amount <> 0 |
> |
Maior que | total_amount > 100 |
< |
Menor que | total_amount < 50 |
>= |
Maior ou igual | total_amount >= 100 |
<= |
Menor ou igual | total_amount <= 500 |
▶ Exemplo: Filtrar Pedidos Concluídos
SELECT order_id, customer_id, total_amount
FROM orders
WHERE order_status = 'completed';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) Filtragem por Data
A comparação de datas é uma das necessidades de filtragem mais comuns em e-commerce.
▶ Exemplo: Pedidos dos Últimos 30 Dias
SELECT order_id, customer_id, total_amount, order_date
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '30 days';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) Combinando Múltiplas Condições
Use AND, OR, NOT para combinar condições. Precedência: NOT > AND > OR; use parênteses para controlar explicitamente.
▶ Exemplo: Filtro com Condição Combinada
SELECT order_id, total_amount, order_status
FROM orders
WHERE order_status = 'completed'
AND total_amount > 500
AND order_date >= '2025-01-01';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
6. Conceito: Ordenação com ORDER BY
(1) Ordenação Básica
ORDER BY por padrão é crescente (ASC); você pode especificar decrescente (DESC). Suporta ordenação por nome de coluna, alias ou posição da coluna.
| Estilo de ordenação | Sintaxe | Descrição |
|---|---|---|
| Crescente | ORDER BY col ASC |
Padrão, ASC é opcional |
| Decrescente | ORDER BY col DESC |
Do maior para o menor |
| Por alias | ORDER BY amount DESC |
Usa alias do SELECT |
| Por posição | ORDER BY 3 DESC |
Pela 3ª coluna na lista SELECT (não recomendado) |
▶ Exemplo: Ordenar por Valor Decrescente
SELECT order_id, customer_id, total_amount
FROM orders
ORDER BY total_amount DESC;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) Ordenação por Múltiplas Colunas
Com ordenação por múltiplas colunas, as linhas são ordenadas pela primeira coluna; empates são resolvidos pela segunda coluna, e assim por diante.
▶ Exemplo: Ordenar por Status, depois por Valor
SELECT order_id, order_status, total_amount
FROM orders
ORDER BY order_status ASC, total_amount DESC;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) NULLS FIRST / NULLS LAST: Recurso do PostgreSQL
O PostgreSQL trata NULL como o maior valor por padrão (último na ordenação crescente, primeiro na decrescente). Você pode controlar explicitamente a posição dos NULLs com NULLS FIRST e NULLS LAST.
| Ordenação | Posição padrão do NULL | Ajustável para |
|---|---|---|
ASC |
NULL no final | NULLS FIRST move NULL para o início |
DESC |
NULL no início | NULLS LAST move NULL para o final |
▶ Exemplo: Controle com NULLS FIRST/LAST
SELECT product_name, discount_rate
FROM products
ORDER BY discount_rate DESC NULLS LAST;
Output:
count
-------
5
(1 row)
7. Conceito: Paginação com LIMIT e FETCH FIRST
(1) LIMIT / OFFSET
LIMIT limita o número de linhas retornadas; OFFSET pula um determinado número de linhas. Juntos, implementam a paginação.
▶ Exemplo: 50 por página, página 3
SELECT order_id, customer_id, total_amount, order_date
FROM orders
WHERE order_status = 'completed'
ORDER BY total_amount DESC
LIMIT 50 OFFSET 100;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) FETCH FIRST: Sintaxe Padrão SQL
O PostgreSQL também suporta a sintaxe padrão SQL FETCH FIRST, semanticamente idêntica ao LIMIT.
| Sintaxe | Equivalente | Descrição |
|---|---|---|
LIMIT 10 |
FETCH FIRST 10 ROWS ONLY |
Forma padrão |
LIMIT 10 OFFSET 20 |
OFFSET 20 ROWS FETCH FIRST 10 ROWS ONLY |
Forma padrão de paginação |
▶ Exemplo: Paginação com FETCH FIRST
SELECT order_id, customer_id, total_amount
FROM orders
ORDER BY total_amount DESC
OFFSET 100 ROWS
FETCH FIRST 50 ROWS ONLY;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) Fórmula de Paginação
| Parâmetro | Cálculo |
|---|---|
Página page |
Começa em 1 |
Tamanho da página size |
ex.: 50 |
OFFSET |
(page - 1) * size |
LIMIT |
size |
▶ Exemplo: Paginação Dinâmica (pseudocódigo)
page=3
size=50
offset=$(( (page - 1) * size ))
psql -c "SELECT order_id, total_amount FROM orders ORDER BY total_amount DESC LIMIT $size OFFSET $offset;"
Output:
# comando psql executado com sucesso
8. Fluxograma: Ordem de Execução da Consulta SELECT
flowchart TD
A[FROM orders] --> B[filtro WHERE]
B --> C[colunas / expressões SELECT]
C --> D[DISTINCT]
D --> E[ordenação ORDER BY]
E --> F[paginação LIMIT / OFFSET]
F --> G[Conjunto de Resultados]
style A fill:#e1f5fe
style B fill:#fff3e0
style C fill:#e8f5e9
style D fill:#f3e5f5
style E fill:#fce4ec
style F fill:#e0f2f1
style G fill:#c8e6c9
9. Exemplo Completo
Bob concluiu as solicitações da equipe de operações: consultar pedidos concluídos dos últimos 30 dias, paginar por valor decrescente e reportar contagens de clientes distintos.
-- Passo 1: Pedidos concluídos dos últimos 30 dias, paginados por valor DESC
SELECT
order_id,
customer_id,
total_amount,
total_amount * 0.08 AS tax_amount,
order_date AS "Order Date",
order_status
FROM orders
WHERE order_status = 'completed'
AND order_date >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY total_amount DESC NULLS LAST
LIMIT 50 OFFSET 0;
-- Passo 2: Contar clientes distintos nesses pedidos
SELECT COUNT(DISTINCT customer_id) AS unique_customers
FROM orders
WHERE order_status = 'completed'
AND order_date >= CURRENT_DATE - INTERVAL '30 days';
-- Passo 3: Último pedido de cada cliente (DISTINCT ON)
SELECT DISTINCT ON (customer_id)
customer_id,
order_id,
total_amount,
order_date
FROM orders
WHERE order_status = 'completed'
AND order_date >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY customer_id, total_amount DESC;
-- Passo 4: Top 10 pedidos com total calculado (preço + imposto)
SELECT
order_id,
customer_id,
total_amount,
ROUND(total_amount * 1.08, 2) AS total_with_tax,
order_date
FROM orders
WHERE order_status = 'completed'
AND order_date >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY total_with_tax DESC
FETCH FIRST 10 ROWS ONLY;
❓ Perguntas Frequentes
P: Posso usar SELECT * em produção? R: Não recomendado. SELECT * retorna todas as colunas, adicionando sobrecarga de transferência de rede e memória, e pode quebrar a lógica da aplicação quando o esquema da tabela muda. Sempre liste explicitamente as colunas que você precisa.
P: Qual a diferença entre DISTINCT e GROUP BY para desduplicação? R: DISTINCT apenas remove linhas duplicadas; GROUP BY também pode fazer agregação. Se você apenas desduplica sem agregar, DISTINCT é mais intuitivo; use GROUP BY quando precisar de agregação.
P: Como a paginação LIMIT OFFSET se comporta com grandes offsets? R: OFFSET precisa pular as primeiras N linhas, então quanto maior o offset, mais lento fica. Para grandes conjuntos de dados, recomenda-se a paginação por chave (filtragem WHERE em uma chave de ordenação), ex.: WHERE order_id > last_seen_id LIMIT 50.
P: NULLS FIRST/LAST é SQL padrão? R: Não, é uma extensão do PostgreSQL. MySQL e SQL Server não suportam; você emularia com CASE ou COALESCE.
P: DISTINCT ON pode ser usado sem ORDER BY? R: Sintaticamente sim, mas sem ORDER BY, qual linha é mantida por grupo é indefinido. Você deve combiná-lo com ORDER BY para resultados previsíveis.
P: Um alias de coluna pode ser usado no WHERE? R: Não. Na ordem de execução SQL, WHERE executa antes de SELECT, então o alias ainda não existe. Mas aliases podem ser usados em ORDER BY, já que ORDER BY executa depois de SELECT.
P: FETCH FIRST e LIMIT são completamente equivalentes? R: Funcionalmente idênticos. FETCH FIRST é a sintaxe padrão SQL:2008; LIMIT é a sintaxe tradicional do PostgreSQL. Para código novo, FETCH FIRST melhora a portabilidade.
P: ORDER BY pode referenciar uma coluna que não está na lista SELECT? R: Sim. O PostgreSQL permite que ORDER BY referencie uma coluna não presente no SELECT, mas se DISTINCT ou GROUP BY for usado, a coluna de ordenação deve aparecer no SELECT ou GROUP BY.
📖 Resumo
SELECTé a base das consultas SQL, suportando nomes de colunas, expressões e aliasesDISTINCTremove linhas duplicadas;DISTINCT ONé sintaxe específica do PostgreSQLWHEREfiltra linhas com operadores de comparação e suporta combinaçõesAND/OR/NOTORDER BYsuportaASC/DESCe o exclusivoNULLS FIRST/LASTdo PostgreSQLLIMIT/OFFSETeFETCH FIRSTimplementam paginação; offsets grandes exigem paginação por chave- Ordem lógica de execução SQL: FROM → WHERE → SELECT → DISTINCT → ORDER BY → LIMIT
📝 Exercícios
-
⭐ Escreva uma consulta selecionando
product_nameeunit_pricedeproducts, ordenada por preço crescente, retornando apenas as primeiras 10 linhas. -
⭐⭐ Escreva uma consulta selecionando pedidos dos últimos 7 dias de
orders, filtrando portotal_amount > 200, ordenados pororder_date DESC, usandoFETCH FIRST 20 ROWS ONLY. -
⭐⭐⭐ Use
DISTINCT ONpara consultar o pedido de maior valor de cadacustomer_id(dica:ORDER BY customer_id, total_amount DESC), e conte quantos desses clientes têm um pedido máximo único acima de 1.000 USD.