Início / Trilha 1 / Conteúdo / Tópico 10
10

SELECT Avançado

DUAL, aliases, prefixação, ORDER BY, DISTINCT, ROWNUM e paginação

📖

Tabela DUAL

O que é a tabela DUAL?

A DUAL é uma tabela especial do Oracle que contém apenas uma linha e uma coluna (chamada DUMMY com valor 'X'). Ela é útil quando você precisa executar funções ou expressões sem consultar uma tabela real.

Usos da DUAL:

  • Testar funções SQL (matemáticas, strings, datas)
  • Obter valores do sistema (data atual, usuário, etc.)
  • Avaliar expressões e cálculos
  • Gerar sequências ou valores constantes
-- Exemplos básicos com DUAL
SELECT SYSDATE FROM DUAL;                    -- Data/hora atual
SELECT USER FROM DUAL;                       -- Usuário conectado
SELECT 10 + 5 AS resultado FROM DUAL;        -- Cálculo simples
SELECT 'Hello, Oracle!' AS mensagem FROM DUAL;  -- Literal
SELECT UPPER('oracle database') FROM DUAL;   -- Função de string
🏷️

Aliases de Colunas e Tabelas

Aliases de Colunas

Aliases permitem renomear colunas no resultado da consulta, tornando-o mais legível. Você pode usar AS (opcional) ou aspas duplas para nomes com espaços.

-- Aliases de colunas
SELECT
    id_funcionario AS codigo,                -- AS é opcional
    nome funcionario,                        -- Sem AS
    salario AS "Salário Mensal",            -- Com espaço (requer aspas)
    salario * 12 AS salario_anual,          -- Expressão calculada
    UPPER(email) email_maiusculo
FROM funcionarios;

-- Resultado será exibido com os nomes dos aliases

Aliases de Tabelas e Prefixação

Aliases de tabelas simplificam queries complexas com múltiplas tabelas. A prefixação (tabela.coluna) evita ambiguidade quando colunas têm o mesmo nome em tabelas diferentes.

-- Aliases de tabelas (muito útil em JOINs)
SELECT
    f.id_funcionario,          -- Prefixo 'f' identifica a tabela
    f.nome AS funcionario,
    f.salario,
    d.nome AS departamento,    -- Prefixo 'd' identifica a tabela
    d.localizacao
FROM funcionarios f            -- Alias 'f' para funcionarios
INNER JOIN departamentos d     -- Alias 'd' para departamentos
    ON f.departamento_id = d.id_departamento
WHERE d.localizacao = 'São Paulo'
ORDER BY f.nome;

-- Sem aliases seria mais verboso:
-- funcionarios.nome, departamentos.nome, etc.

Boa Prática

Sempre use prefixos (aliases de tabela) em queries com múltiplas tabelas, mesmo quando não há ambiguidade. Isso torna o código mais claro e evita erros futuros se a estrutura mudar.

🔢

ORDER BY - Ordenação de Resultados

Ordenação Ascendente e Descendente

ORDER BY ordena o resultado da consulta. Use ASC (ascendente, padrão) ou DESC (descendente).

-- Ordem ascendente (padrão)
SELECT nome, salario
FROM funcionarios
ORDER BY salario;              -- Menor para maior (ASC implícito)

-- Ordem descendente
SELECT nome, salario
FROM funcionarios
ORDER BY salario DESC;         -- Maior para menor

-- Múltiplas colunas
SELECT nome, departamento_id, salario
FROM funcionarios
ORDER BY departamento_id ASC, salario DESC;  -- Dept crescente, salário decrescente

-- Ordenar por posição da coluna no SELECT
SELECT nome, salario, data_admissao
FROM funcionarios
ORDER BY 3 DESC, 2 DESC;       -- Posição 3 (data_admissao), depois posição 2 (salario)

-- Ordenar por alias
SELECT nome AS funcionario, salario * 12 AS salario_anual
FROM funcionarios
ORDER BY salario_anual DESC;   -- Usa o alias definido no SELECT

Atenção: NULLs na Ordenação

No Oracle, valores NULL são considerados "maiores" que qualquer valor.

  • ORDER BY coluna ASC - NULLs aparecem no final
  • ORDER BY coluna DESC - NULLs aparecem no início
  • Use NULLS FIRST ou NULLS LAST para controlar
-- Controlar posição dos NULLs
SELECT nome, data_demissao
FROM funcionarios
ORDER BY data_demissao NULLS LAST;  -- NULLs no final
🎯

DISTINCT - Eliminar Duplicatas

Removendo Linhas Duplicadas

DISTINCT remove linhas duplicadas do resultado, considerando todas as colunas do SELECT.

-- Sem DISTINCT - pode retornar valores repetidos
SELECT departamento_id
FROM funcionarios;
-- Resultado: 10, 20, 10, 30, 20, 10 (duplicatas)

-- Com DISTINCT - valores únicos
SELECT DISTINCT departamento_id
FROM funcionarios;
-- Resultado: 10, 20, 30 (sem duplicatas)

-- DISTINCT em múltiplas colunas (combinação única)
SELECT DISTINCT departamento_id, cargo
FROM funcionarios;
-- Retorna combinações únicas de departamento + cargo

-- DISTINCT com COUNT
SELECT COUNT(DISTINCT departamento_id) AS total_departamentos
FROM funcionarios;
-- Conta quantos departamentos diferentes existem

Performance

DISTINCT requer ordenação/comparação de todos os registros, o que pode ser custoso em grandes volumes. Use apenas quando realmente necessário. Considere se um GROUP BY seria mais apropriado.

📄

ROWNUM e Paginação Básica

O que é ROWNUM?

ROWNUM é uma pseudocoluna que atribui um número sequencial para cada linha no momento em que é retornada pela query.

-- Limitar número de linhas retornadas
SELECT ROWNUM, nome, salario
FROM funcionarios
WHERE ROWNUM <= 5;             -- Retorna apenas as 5 primeiras linhas

-- Top 3 maiores salários (requer subquery)
SELECT *
FROM (
    SELECT nome, salario
    FROM funcionarios
    ORDER BY salario DESC      -- Ordenar ANTES de aplicar ROWNUM
)
WHERE ROWNUM <= 3;             -- Pegar os 3 primeiros da ordenação

Cuidado: ROWNUM é atribuído ANTES da ordenação

ROWNUM é atribuído quando a linha é retornada do FROM/WHERE, antes do ORDER BY. Para paginar dados ordenados, use subquery.

-- INCORRETO - ORDER BY é aplicado DEPOIS do ROWNUM
SELECT ROWNUM, nome, salario
FROM funcionarios
WHERE ROWNUM <= 3
ORDER BY salario DESC;         -- Ordena DEPOIS de pegar as 3 primeiras

-- CORRETO - Subquery para ordenar ANTES do ROWNUM
SELECT *
FROM (
    SELECT nome, salario
    FROM funcionarios
    ORDER BY salario DESC      -- Ordena primeiro
)
WHERE ROWNUM <= 3;             -- Depois pega as 3 primeiras

Paginação com ROWNUM

Para paginar (pular linhas e pegar um intervalo), use subquery dupla:

-- Página 1: linhas 1-10
SELECT *
FROM (
    SELECT nome, salario
    FROM funcionarios
    ORDER BY nome
)
WHERE ROWNUM <= 10;

-- Página 2: linhas 11-20 (requer subquery dupla)
SELECT *
FROM (
    SELECT ROWNUM AS rn, a.*
    FROM (
        SELECT nome, salario
        FROM funcionarios
        ORDER BY nome          -- Ordenação desejada
    ) a
    WHERE ROWNUM <= 20         -- Limite superior
)
WHERE rn > 10;                 -- Limite inferior (pular primeiras 10)

-- Alternativa moderna: ROW_NUMBER() (Oracle 12c+)
SELECT nome, salario
FROM (
    SELECT nome, salario,
           ROW_NUMBER() OVER (ORDER BY nome) AS rn
    FROM funcionarios
)
WHERE rn BETWEEN 11 AND 20;    -- Página 2 (linhas 11-20)

Oracle 12c+: OFFSET e FETCH

A partir do Oracle 12c, você pode usar a sintaxe SQL padrão OFFSET/FETCH:

-- Sintaxe moderna (Oracle 12c+)
SELECT nome, salario
FROM funcionarios
ORDER BY nome
OFFSET 10 ROWS                 -- Pular 10 linhas
FETCH NEXT 10 ROWS ONLY;       -- Pegar as próximas 10
🛠️

Exemplos Práticos Completos

Combinando Técnicas

-- Exemplo 1: Top 5 departamentos com mais funcionários
SELECT departamento, total_funcionarios
FROM (
    SELECT
        d.nome AS departamento,
        COUNT(f.id_funcionario) AS total_funcionarios
    FROM departamentos d
    LEFT JOIN funcionarios f ON d.id_departamento = f.departamento_id
    GROUP BY d.nome
    ORDER BY total_funcionarios DESC
)
WHERE ROWNUM <= 5;

-- Exemplo 2: Listar funcionários únicos por cargo (sem duplicatas)
SELECT DISTINCT
    f.cargo,
    d.nome AS departamento
FROM funcionarios f
INNER JOIN departamentos d ON f.departamento_id = d.id_departamento
ORDER BY f.cargo, d.nome;

-- Exemplo 3: Relatório com múltiplas ordenações e aliases
SELECT
    ROWNUM AS posicao,
    f.nome AS "Nome do Funcionário",
    f.salario AS "Salário Atual",
    f.salario * 12 AS "Salário Anual",
    d.nome AS "Departamento"
FROM (
    SELECT f.*, d.nome AS dept_nome
    FROM funcionarios f
    INNER JOIN departamentos d ON f.departamento_id = d.id_departamento
    ORDER BY d.nome, f.salario DESC
) f, departamentos d
WHERE f.departamento_id = d.id_departamento
  AND ROWNUM <= 20;

-- Exemplo 4: Contagem de funcionários por cidade (distintos)
SELECT
    d.localizacao AS cidade,
    COUNT(DISTINCT f.id_funcionario) AS total_funcionarios,
    COUNT(DISTINCT f.cargo) AS cargos_diferentes
FROM departamentos d
LEFT JOIN funcionarios f ON d.id_departamento = f.departamento_id
GROUP BY d.localizacao
ORDER BY total_funcionarios DESC NULLS LAST;

Resultado Esperado

O que você deve dominar após este tópico:

Usar DUAL efetivamente

Executar funções e expressões sem tabelas reais

Criar aliases claros

Tornar queries mais legíveis com nomes apropriados

Ordenar resultados

ORDER BY com múltiplas colunas e direções

Eliminar duplicatas

Usar DISTINCT apropriadamente

Limitar resultados

ROWNUM para top-N queries

Implementar paginação

Técnicas de paginação com ROWNUM e OFFSET/FETCH

📝

Exercícios Práticos

Exercício 1: Top 10 Salários

Crie uma query que retorne os 10 funcionários com maiores salários, mostrando posição, nome, cargo e salário, ordenados do maior para o menor salário.

Ver Solução
SELECT
    ROWNUM AS posicao,
    nome,
    cargo,
    salario
FROM (
    SELECT nome, cargo, salario
    FROM funcionarios
    ORDER BY salario DESC
)
WHERE ROWNUM <= 10;

Exercício 2: Listar Cidades Únicas

Liste todas as cidades únicas onde existem departamentos, ordenadas alfabeticamente.

Ver Solução
SELECT DISTINCT localizacao AS cidade
FROM departamentos
WHERE localizacao IS NOT NULL
ORDER BY localizacao;

Exercício 3: Paginação de Funcionários

Implemente uma query que retorne a página 3 (linhas 21-30) de funcionários ordenados por nome.

Ver Solução
-- Método com ROWNUM (Oracle < 12c)
SELECT *
FROM (
    SELECT ROWNUM AS rn, a.*
    FROM (
        SELECT id_funcionario, nome, cargo, salario
        FROM funcionarios
        ORDER BY nome
    ) a
    WHERE ROWNUM <= 30
)
WHERE rn > 20;

-- Método moderno (Oracle 12c+)
SELECT id_funcionario, nome, cargo, salario
FROM funcionarios
ORDER BY nome
OFFSET 20 ROWS
FETCH NEXT 10 ROWS ONLY;
Anterior: DML Próximo: TCL