[SQL Intermediário] Conquiste as Subconsultas! Um Guia Completo sobre SELECT dentro de SELECT
"Quero encontrar apenas os funcionários que ganham mais que o salário médio da empresa..."
"Quem é o funcionário com o salário mais alto em cada departamento?"
Como um criador web que trabalha com bancos de dados, você já quis "usar o resultado de uma consulta como condição para outra"? Esse tipo de extração de dados um pouco mais complexa não pode ser feita em uma única etapa. A solução elegante para este problema é o que abordaremos hoje: a subconsulta.
Resumidamente, uma subconsulta é uma "consulta dentro de outra consulta". É uma técnica em que você aninha uma instrução SELECT dentro de outra, como um conjunto de bonecas Matryoshka. Uma vez que você dominar isso, poderá consolidar a extração de dados que antes levava várias etapas em uma única consulta, o que elevará significativamente suas habilidades em SQL!
Este artigo está repleto de exemplos de código que até mesmo iniciantes podem copiar e colar para fazê-los funcionar. Queremos que você experimente aquele momento de "funcionou!" para sentir verdadeiramente o poder e a conveniência das subconsultas.
Preparação: Nossas Tabelas de Prática para Hoje
Para experimentar plenamente o poder das subconsultas, desta vez prepararemos duas tabelas: uma tabela "employees" (funcionários) e uma tabela "departments" (departamentos). Por favor, copie a instrução SQL abaixo e execute-a em seu próprio ambiente de banco de dados.
Claro, não há problema se você не tiver um ambiente pronto! Mais adiante no artigo, preparamos um "ambiente de execução de SQL" onde você pode experimentar o SQL usando apenas o seu navegador.
-- Excluir tabelas se existirem
DROP TABLE IF EXISTS employees;
DROP TABLE IF EXISTS departments;
-- Criar a tabela de departamentos
CREATE TABLE departments (
id INT PRIMARY KEY,
department_name VARCHAR(50)
);
-- Inserir dados dos departamentos
INSERT INTO departments (id, department_name) VALUES
(1, '営業部'),
(2, '開発部'),
(3, '人事部');
-- Criar a tabela de funcionários
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(50),
department_id INT,
salary INT,
FOREIGN KEY (department_id) REFERENCES departments(id)
);
-- Inserir dados dos funcionários
INSERT INTO employees (id, name, department_id, salary) VALUES
(1, '山田 太郎', 1, 600000), -- Dpto. de Vendas
(2, '鈴木 花子', 2, 550000), -- Dpto. de Desenvolvimento
(3, '佐藤 次郎', 1, 400000), -- Dpto. de Vendas
(4, '高橋 三郎', 3, 650000), -- Dpto. de RH
(5, '田中 恵子', 2, 500000), -- Dpto. de Desenvolvimento
(6, '伊藤 四郎', 1, 700000), -- Dpto. de Vendas
(7, '渡辺 久美', 2, 750000), -- Dpto. de Desenvolvimento
(8, '山本 五郎', 3, 380000); -- Dpto. de RH
Tudo pronto? Então vamos entrar no poderoso mundo das subconsultas!
1. O Básico: Subconsultas na Cláusula `WHERE`
A maneira mais básica e intuitiva de usar uma subconsulta é como condição na cláusula WHERE. Pense nela como uma forma de gerar dinamicamente os critérios de filtragem para a consulta externa (a consulta principal) usando o resultado de uma consulta interna (a subconsulta).
É uma regra que as subconsultas devem sempre estar entre () (parênteses). O SQL primeiro executa a subconsulta dentro dos parênteses e depois usa o resultado como condição para a consulta principal.
Exemplo 1: Extraindo Funcionários com Salários Maiores que a Média
Vamos tentar encontrar os "funcionários que ganham mais que o salário médio de todos os funcionários", que mencionamos no início. Para conseguir isso, você precisa de duas etapas:
- Primeiro, calcular o "salário médio de todos os funcionários".
- Em seguida, usar esse salário médio como condição para extrair as informações dos funcionários.
Com uma subconsulta, você pode fazer isso em uma única consulta.
SELECT *
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
Que tal? Primeiro, (SELECT AVG(salary) FROM employees) é executado para calcular o salário médio (547500 neste exemplo), e esse resultado é então usado como condição para a consulta principal, como se fosse WHERE salary > 547500. Você alcançou brilhantemente o objetivo com apenas uma consulta!
Exemplo 2: Extraindo Funcionários em Departamentos Específicos (com o Operador IN)
Uma subconsulta pode retornar não apenas um único valor, mas também uma lista de múltiplos valores. Ao combinar essa lista com o operador IN, você pode alcançar condições incrivelmente flexíveis.
Por exemplo, digamos que você queira extrair todos os funcionários que pertencem aos departamentos de 'Vendas' ou 'RH'. Vamos usar uma subconsulta para obter uma lista de IDs de departamento a partir dos nomes dos departamentos e usar isso como condição para a consulta principal.
SELECT *
FROM employees
WHERE department_id IN (
SELECT id FROM departments WHERE department_name IN ('営業部', '人事部')
);
Nesta consulta, a subconsulta é executada primeiro e retorna uma lista de IDs para '営業部' (Vendas) e '人事部' (RH), que é (1, 3). A consulta principal é então interpretada como WHERE department_id IN (1, 3) e extrai os dados dos funcionários correspondentes. Isso é muito conveniente porque você не precisa modificar o SQL mesmo que os nomes dos departamentos mudem.
2. Uso Avançado: Subconsultas nas Cláusulas `SELECT` e `FROM`
As subconsultas не são apenas para a cláusula WHERE. Elas são especialmente úteis nas cláusulas SELECT e FROM, expandindo enormemente as possibilidades da sua análise de dados.
Subconsultas na cláusula SELECT (Subconsulta escalar)
Quando você usa uma subconsulta na cláusula SELECT, pode obter um valor relacionado para cada linha da consulta principal e exibi-lo como uma nova coluna. No entanto, este tipo de subconsulta deve sempre retornar um único valor (um valor escalar) de uma linha e uma coluna.
Como exemplo, vamos exibir as informações de cada funcionário juntamente com "o salário médio do departamento ao qual pertencem".
SELECT
name,
salary,
(SELECT department_name FROM departments WHERE id = e.department_id) AS department_name,
(SELECT AVG(salary) FROM employees WHERE department_id = e.department_id) AS department_avg_salary
FROM
employees AS e;
Parece um pouco complexo, não é? Isso é chamado de subconsulta correlacionada, onde a subconsulta interna faz referência a dados de cada linha da consulta externa (neste caso, e.department_id) para ser executada. Para cada linha que a consulta principal processa, o nome do departamento e o salário médio do departamento são calculados usando o ID do departamento daquela linha e adicionados como colunas ao resultado.
Subconsultas na cláusula FROM (Visão em linha)
Quando você usa uma subconsulta na cláusula FROM, pode tratar o resultado dessa subconsulta como uma "tabela virtual temporária (uma visão em linha)". Isso é útil para processamento em duas etapas, onde você primeiro agrega ou processa dados com uma subconsulta e depois executa outra consulta sobre esse resultado.
Como exemplo, vamos "calcular o salário médio por departamento e, em seguida, exibir apenas os departamentos onde o salário médio é de 550.000 ou mais".
SELECT
d.department_name,
dept_avg.avg_salary
FROM
(
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
) AS dept_avg
JOIN
departments AS d ON dept_avg.department_id = d.id
WHERE
dept_avg.avg_salary >= 550000;
Nesta consulta, a subconsulta na cláusula FROM é executada primeiro, criando uma tabela temporária chamada dept_avg que contém o salário médio para cada department_id. Em seguida, a consulta principal junta esta tabela dept_avg com a tabela departments para exibir os nomes e salários médios dos departamentos que atendem à condição. Esta é uma técnica muito eficaz quando você quer usar uma cláusula WHERE no resultado de uma operação GROUP BY.
Prática! Vamos Executar SQL no seu Navegador
Obrigado por esperar! Aqui está um ambiente onde você pode colocar a mão na massa и experimentar as subconsultas que aprendeu até agora.
Copie o bloco de código inteiro abaixo, salve-o como um arquivo com um nome como subquery_practice.html e abra-o no seu navegador. Experimente as várias subconsultas deste artigo para ter uma ideia de como elas funcionam!
<!DOCTYPE html>
<html lang="pt">
<head>
<meta charset="UTF-8">
<title>Ambiente de Testes para Subconsultas SQL</title>
<script src="https://cdn.jsdelivr.net/npm/alasql@4"></script>
<style>
body { font-family: sans-serif; padding: 2rem; background-color: #f9f9f9; }
.container { max-width: 800px; margin: auto; background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
h1 { color: #333; }
textarea { width: 100%; height: 180px; font-family: monospace; font-size: 16px; padding: 10px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; margin-bottom: 1rem; }
button { background-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
button:hover { background-color: #0056b3; }
#result-area { margin-top: 2rem; }
table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.error { color: red; font-weight: bold; }
</style>
</head>
<body>
<div class="container">
<h1>Vamos Experimentar as Subconsultas!</h1>
<p>Insira uma instrução SQL na área de texto abaixo e pressione o botão "Executar SQL".</p>
<textarea id="sql-input">-- Escreva seu SQL aqui
SELECT
d.department_name,
dept_avg.avg_salary
FROM
(
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
) AS dept_avg
JOIN
departments AS d ON dept_avg.department_id = d.id
WHERE
dept_avg.avg_salary >= 550000;</textarea>
<button onclick="executeSQL()">Executar SQL</button>
<div id="result-area"></div>
</div>
<script>
// Inicializar o banco de dados e preparar os dados
const db = new alasql.Database();
db.exec(`
CREATE TABLE departments (id INT PRIMARY KEY, department_name VARCHAR(50));
INSERT INTO departments VALUES (1, '営業部'), (2, '開発部'), (3, '人事部');
CREATE TABLE employees (id INT PRIMARY KEY, name VARCHAR(50), department_id INT, salary INT);
INSERT INTO employees VALUES
(1, '山田 太郎', 1, 600000), (2, '鈴木 花子', 2, 550000), (3, '佐藤 次郎', 1, 400000),
(4, '高橋 三郎', 3, 650000), (5, '田中 恵子', 2, 500000), (6, '伊藤 四郎', 1, 700000),
(7, '渡辺 久美', 2, 750000), (8, '山本 五郎', 3, 380000);
`);
function executeSQL() {
const sql = document.getElementById('sql-input').value;
const resultArea = document.getElementById('result-area');
resultArea.innerHTML = '';
try {
const result = db.exec(sql);
if (result.length > 0) {
resultArea.appendChild(createTable(result));
} else {
resultArea.innerHTML = '<p>A consulta retornou 0 linhas.</p>';
}
} catch (e) {
resultArea.innerHTML = `<p class="error">Erro: ${e.message}</p>`;
}
}
function createTable(data) {
const table = document.createElement('table');
const thead = table.createTHead();
const tbody = table.createTBody();
const headerRow = thead.insertRow();
for (const key in data[0]) {
const th = document.createElement('th');
th.textContent = key;
headerRow.appendChild(th);
}
data.forEach(rowData => {
const row = tbody.insertRow();
for (const key in rowData) {
const cell = row.insertCell();
cell.textContent = rowData[key];
}
});
return table;
}
// Exibição inicial
executeSQL();
</script>
</body>
</html>
Pontos de Atenção e Técnicas Relacionadas
As subconsultas são extremamente poderosas, mas há algumas coisas a ter em mente. Conhecê-las ajudá-lo-á a escrever consultas mais sofisticadas.
Problemas de Desempenho
As subconsultas, especialmente as subconsultas correlacionadas que acabamos de apresentar, podem degradar significativamente o desempenho à medida que a quantidade de dados aumenta, porque são executadas para cada linha da consulta principal. Em muitos casos, uma subconsulta pode ser reescrita usando um JOIN, que muitas vezes é executado mais rapidamente.
Valores de Retorno da Subconsulta
Existem regras sobre a forma dos dados que uma subconsulta pode retornar.
- Quando usada com operadores de comparação como
=,>,<, a subconsulta deve retornar um valor único (um valor escalar). - Quando usada com o operador
IN, pode retornar uma lista de uma única coluna. - Quando usada na cláusula
SELECT, também deve ser um valor único.
Quebrar estas regras causará um erro, por isso é importante estar sempre ciente do tipo de resultado que a sua subconsulta irá retornar.
Técnica Relacionada: Reescrevendo com `JOIN`
Do ponto de vista do desempenho, recomenda-se reescrever muitas subconsultas usando um JOIN. Vamos tentar reescrever a consulta "extrair funcionários em departamentos específicos" usando um JOIN.
SELECT
e.*
FROM
employees AS e
JOIN
departments AS d ON e.department_id = d.id
WHERE
d.department_name IN ('営業部', '人事部');
Esta consulta usando JOIN retorna o mesmo resultado que a que usa uma subconsulta. Geralmente, o otimizador de um banco de dados (o recurso que otimiza as consultas) pode processar JOINs de forma mais eficiente, então você deve considerar usar um JOIN, especialmente ao lidar com grandes quantidades de dados.
Resumo
Ótimo trabalho! Hoje, mergulhamos fundo na poderosa técnica de SQL das subconsultas.
- Subconsultas na cláusula
WHERE: O caso de uso mais básico para gerar dinamicamente condições de consulta. - Subconsultas na cláusula
SELECT: Para obter um valor relacionado para cada linha e adicioná-lo como uma nova coluna (subconsulta correlacionada). - Subconsultas na cláusula
FROM: Para tratar o resultado de uma consulta como uma tabela temporária para consultas mais complexas. - Desempenho: As subconsultas são convenientes, mas é importante considerar sempre reescrevê-las com
JOINs.
Dominar as subconsultas permite que você realize em uma única etapa a recuperação de dados que antes exigia múltiplos passos, melhorando drasticamente a sua eficiência de trabalho. Por favor, use o ambiente de execução de SQL que fornecemos para experimentar vários padrões e tornar as subconsultas suas!