Consultas SQL
Seleção, junções, NULL, agregação e resultados sem duplicações acidentais.
Linhas, filtros e ordem
FROM e junções definem as linhas; WHERE filtra-as; SELECT escolhe ou calcula colunas; ORDER BY ordena.
- SQL conserva duplicados por omissão;
DISTINCTelimina repetições da combinação completa. Não usesDISTINCTpara esconder uma junção errada: agrupa por chave, não por nome. - Só
ORDER BYgarante ordem. Com empates, define desempate para uma linha ou conserva todos os máximos em vez deLIMIT 1isolado. BETWEENinclui limites; emLIKE,%aceita qualquer sequência e_um carácter. Maiúsculas dependem do motor e da collation;ANDprecedeOR, por isso explicita grupos com parênteses.
Junções sem multiplicar mal
JOIN ... ON conserva correspondências; LEFT JOIN conserva a esquerda, com NULL à direita sem correspondência. Condição no ON escolhe correspondências; no WHERE, filtra o resultado.
SELECT c.id, c.nome, COUNT(e.id) AS encomendas
FROM Cliente AS c
LEFT JOIN Encomenda AS e ON e.idCliente = c.id
GROUP BY c.id, c.nome
ORDER BY c.id;PRAGMA foreign_keys = ON;
CREATE TABLE Cliente (
id INTEGER PRIMARY KEY,
nome TEXT NOT NULL,
email TEXT UNIQUE
);
CREATE TABLE Produto (
id INTEGER PRIMARY KEY,
nome TEXT NOT NULL,
precoCentimos INTEGER NOT NULL CHECK (precoCentimos >= 0),
stock INTEGER NOT NULL CHECK (stock >= 0)
);
CREATE TABLE Encomenda (
id INTEGER PRIMARY KEY,
data TEXT NOT NULL,
idCliente INTEGER NOT NULL REFERENCES Cliente(id)
);
CREATE TABLE Item (
idEncomenda INTEGER NOT NULL REFERENCES Encomenda(id) ON DELETE CASCADE,
idProduto INTEGER NOT NULL REFERENCES Produto(id),
qtd INTEGER NOT NULL CHECK (qtd > 0),
precoUnitario INTEGER NOT NULL CHECK (precoUnitario >= 0),
PRIMARY KEY (idEncomenda, idProduto)
);
INSERT INTO Cliente VALUES
(1, 'Ana', 'ana@example.test'),
(2, 'Rui', NULL),
(3, 'Mia', NULL);
INSERT INTO Produto VALUES
(10, 'Teclado', 4500, 20),
(11, 'Rato', 2500, 50),
(12, 'Monitor', 18000, 5);
INSERT INTO Encomenda VALUES
(100, '2026-01-05', 1),
(101, '2026-01-06', 1),
(102, '2026-01-06', 2);
INSERT INTO Item VALUES
(100, 10, 2, 4500),
(100, 11, 1, 2500),
(101, 12, 1, 18000),
(102, 11, 3, 2500);Executa a consulta para ver as tabelas.
Ana/2, Rui/1, Mia/0: COUNT(e.id) ignora o NULL da linha artificial de Mia, COUNT(*) contá-la-ia. Para filtrar por data sem perder Mia, põe a data no ON, não no WHERE.
- Juntar Item multiplica encomendas por produtos: Ana passa a três linhas de compra. Conta encomendas com
COUNT(DISTINCT e.id)e soma totais já agregados sem voltar a juntar. - Numa autojunção de pares, usa aliases, exige o mesmo cliente e
e1.id < e2.id: exclui autorreferência e a ordem inversa. NATURAL JOINiguala todos os nomes comuns, mesmo com significados diferentes.
NULL a três valores
Comparar com NULL dá desconhecido; WHERE só aceita verdadeiro. Testa com IS NULL e IS NOT NULL.
| Combinação | Resultado |
|---|---|
NOT desconhecido | desconhecido |
falso AND desconhecido | falso |
verdadeiro OR desconhecido | verdadeiro |
restantes com desconhecido | desconhecido |
preco > 30 OR preco <= 30 não cobre preço NULL. COALESCE(x, alt) escolhe o primeiro não nulo; só o tornes zero quando essa for a interpretação certa.
Agregar por grupos
WHERE filtra linhas antes de agrupar; HAVING filtra grupos depois. Para encomendas acima de 100 euros: agrupa Item por idEncomenda e HAVING SUM(qtd * precoUnitario) > 10000.
COUNT(*)conta linhas;COUNT(x)eCOUNT(DISTINCT x)ignoram nulos.SUM,AVG,MIN,MAXignoram nulos: média de 10,NULL, 20 é 15.- Sem linhas e sem
GROUP BY, uma linha comCOUNT0 e restoNULL; comGROUP BY, nenhuma linha. - Colunas da resposta vão no agrupamento ou em agregações; a tolerância de SQLite a colunas soltas não é portátil.
CASE sem ELSE dá NULL quando nada corresponde. Datas ISO uniformes ordenam-se como texto; durações pedem funções de data, como julianday, não subtração de textos.