19th Jul 2022 9 minutos de leitura Como Encontrar Todos os Funcionários de Cada Gerente em SQL Tihomir Babic sql aprender sql consultas recursivas Índice Tabela de Funcionários Contar Todos os Funcionários de Cada Gerente Encontre Todos os Subordinados Diretos de Cada Gerente Encontre os Subordinados Diretos e Indiretos de Cada Gerente Você já ficou encarregado de construir um organograma - ou seja, encontrar os funcionários subordinados a cada gerente de uma empresa? Com o SQL, fazer isso é mais fácil do que você imagina! Neste artigo, mostraremos como realizar a tarefa usando uma consulta recursiva. No mundo dos negócios, é muito comum precisar listar todos os funcionários que trabalham para um determinado gerente. Será que isso é tão difícil a ponto de precisar de um artigo inteiro? Se você pensar em um organograma, parece bem simples: encontrar o gerente, listar todos os seus subordinados e pronto. Qual é a dificuldade? A questão é que o SQL funciona com tabelas, não com organogramas. Quando um organograma se traduz em uma tabela, pode ser um pouco mais complicado de executar o que é pedido. A técnica que usarei neste artigo baseia-se principalmente em SQL JOINs e consultas recursivas. Se você precisar praticar (ou se quiser ver explicações mais detalhadas), confira nossos cursos sobre Cláusulas JOIN em SQL e Consultas Recursivas. Vamos começar analisando como um organograma é normalmente exibido em uma tabela adequada para consultas em SQL. Tabela de Funcionários Usaremos uma tabela chamada funcionario, que tem as seguintes colunas: funcionario_id: Número de identificação do funcionário. nome: Nome do funcionário. sobrenome: Sobrenome do funcionário. gerente_id: Número de identificação do gerente. Neste exemplo, há dez funcionários. Vamos dar uma olhada nos registros: funcionario_idnomesobrenomegerente_id 4529NatáliaJardim4125 4238JoãoSantos4329 4329MartaCardoso4125 4009CláudioCosta4329 4125MelissaRamosNULL 4500JoaquimHolanda4529 4118MariaAssis4952 4012JonasNogueira4952 4952SandraRibeiro4529 4444SamuelQueiroz4329 Nada de outro mundo até aqui, certo? Apenas uma lista de funcionários. A parte mais importante aqui é a coluna gerente_id, que contém a identificação do chefe de cada funcionário. Por exemplo: funcionario_idnomesobrenomegerente_id 4529NatáliaJardim4125 Natália Jardim (funcionario_id 4529) tem um chefe. A identificação do gerente dela é 4125, exibida na coluna gerente_id. Se você procurar por esse valor na coluna funcionario_id, verá que este é o ID de Melissa Ramos. Agora, vamos dar uma olhada na Sra. Melissa Ramos: funcionario_idnomesobrenomegerente_id 4125MelissaRamosNULL Há um valor NULL na coluna gerente_id, o que significa que Melissa Ramos não tem um gerente acima dela - ou seja, ela é a presidente da empresa. A lógica diz que sempre que há um valor NOT NULL na coluna gerente_id, o funcionário responde a um gerente. É claro que é possível que um funcionário possa ser um gerente e também ter alguém acima dele na hierarquia. Contar Todos os Funcionários de Cada Gerente Vamos fazer um exercício de 'aquecimento'. Usando apenas a tabela funcionario, como você contaria todos os funcionários de cada gerente? Aqui vai um exemplo de como você poderia fazer isso, usando o truque de ligar uma tabela a ela mesma (SELF JOIN, ou auto-junção): SELECT sup.funcionario_id, sup.nome, sup.sobrenome, COUNT (sub.funcionario_id) AS numero_de_funcionarios FROM funcionario sub JOIN funcionario sup ON sub.gerente_id = sup.funcionario_id GROUP BY sup.funcionario_id, sup.nome, sup.sobrenome; Se você não está muito familiarizado com SELF JOINs, veja este artigo com explicações e exemplos sobre auto-junção (Self Join). Ao aplicar o SELF JOIN na tabela funcionario, utilizei pseudônimos para ficar mais fácil de acompanhar o processo. Uma tabela se chamará sub (funcionários subordinados) e, a outra, sup (funcionários superiores). Já que estou procurando os dados analíticos dos gerentes, escrevi um comando para que a consulta retorne as colunas funcionario_id, nome e sobrenome da tabela sup. O código então conta o número de funcionários usando a função COUNT() na coluna sub.funcionario_id. Você obteria o mesmo resultado se usasse sup.funcionario_id. Decidi usar a tabela sub para poder explicar esta etapa da seguinte maneira: "Ah! Como a tabela sub é para funcionários subordinados, então, logicamente, é nela que eu conto o número de subordinados". Como eu disse anteriormente, ao ligar a tabela funcionario a ela mesma utilizando o SELF JOIN, utilizei dois pseudônimos. A condição de ligação é sub.gerente_id = sup.funcionario_id. Parece lógico, pois o valor na coluna gerente_id é o número de identificação do gerente como funcionário, que naturalmente estará na coluna funcionario_id. Como utilizei a função de agregação COUNT(), tenho que agrupar o resultado pelas colunas funcionario_id, nome e sobrenome. Ao rodar o código, ele retornará o seguinte resultado: funcionario_idnomesobrenomenumero_de_funcionarios 4125MelissaRamos2 4329MartaCardoso3 4529NatáliaJardim2 4952SandraRibeiro2 A tabela mostra quatro gerentes e o número de subordinados de cada um (numero_de_funcionarios). Encontre Todos os Subordinados Diretos de Cada Gerente Para encontrar subordinados diretos, o processo é parecido com o que descrevemos acima. Se eu encontrei o número de subordinados, então eu também consigo encontrar os nomes desses subordinados. Na verdade, a solução para este exercício é apenas uma variação do código anterior: SELECT sub.funcionario_id AS subordinado_id, sub.nome AS nome_subordinado, sub.sobrenome AS sobrenome_subordinado, sup.funcionario_id AS superior_id, sup.nome AS nome_superior, sup.sobrenome AS sobrenome_superior FROM funcionario sub JOIN funcionario sup ON sub.gerente_id = sup.funcionario_id ORDER BY superior_id; O princípio aqui é o mesmo; a tabela funcionario é ligada a ela mesma através da coluna sub.gerente_id = sup.funcionario_id. Para fazer isso, eu novamente uso os pseudônimos sub e sup. Primeiro, pego as colunas sub.funcionario_id, sub.nome e sub.sobrenome, e renomeio-as, já que os dados se referem aos funcionários subordinados. Em seguida, eu repito o processo com essas colunas na tabela sup, mas desta vez o nome refletirá o cargo superior do funcionário. Finalmente, o resultado é ordenado pela coluna superior_id: subordinado_idnome_subordinadosobrenome_subordinadosuperior_idnome_superiorsobrenome_superior 4329MartaCardoso4125MelissaRamos 4529NatáliaJardim4125MelissaRamos 4238JoãoSantos4329MartaCardoso 4444SamuelQueiroz4329MartaCardoso 4009CláudioCosta4329MartaCardoso 4500JoaquimHolanda4529NatáliaJardim 4952SandraRibeiro4529NatáliaJardim 4118MariaAssis4952SandraRibeiro 4012JonasNogueira4952SandraRibeiro Ao que parece, a tabela mostra o que queríamos. Temos os nomes dos funcionários e os nomes de seus gerentes. No entanto, percebam que há apenas nove funcionários, quando sabemos que há dez funcionários na empresa. Por que isso acontece? A razão é Melissa Ramos: ela é a presidente da empresa e, por isso, não tem um superior e não é subordinada a ninguém. Isto é representado como um valor NULL na coluna gerente_id: funcionario_idnomesobrenomegerente_id 4125MelissaRamosNULL Ela não aparece no resultado da consulta por causa da natureza da auto-junção (SELF JOIN); eu usei JOIN (ou seja, INNER JOIN). Este tipo de junção não retorna linhas com valores NULL. Se você também quiser que essas linhas apareçam no resultado da sua consulta, terá que usar LEFT JOIN ao invés de JOIN; todo o resto da consulta permanece igual. Esta parte do código será parecida com esta: ... FROM funcionario sub LEFT JOIN funcionario sup ON sub.gerente_id = sup.funcionario_id ... A execução do novo código retornará todos os funcionários, mesmo aqueles que não têm um superior: subordinado_idnome_subordinadosobrenome_subordinadosuperior_idnome_superiorsobrenome_superior 4125MelissaRamosNULLNULLNULL 4529NatáliaJardim4125MelissaRamos 4329MartaCardoso4125MelissaRamos 4009CláudioCosta4329MartaCardoso 4238JoãoSantos4329MartaCardoso 4444SamuelQueiroz4329MartaCardoso 4952SandraRibeiro4529NatáliaJardim 4500JoaquimHolanda4529NatáliaJardim 4118MariaAssis4952SandraRibeiro 4012JonasNogueira4952SandraRibeiro No entanto, talvez este resultado ainda não seja satisfatório, pois retorna apenas os subordinados diretos. Em empresas mais complexas, alguns gerentes têm subordinados diretos que gerenciam outros funcionários. Esses funcionários têm um chefe direto, mas também respondem ao chefe de seu chefe. Será que é possível escolher um gerente e obter a lista de todos os seus subordinados diretos e indiretos em SQL? Com certeza, sim! Para isso, recorremos à nossa velha amiga, a consulta recursiva. Se você não estiver muito familiarizado com o conceito de consultas recursivas, recomendamos que leia este artigo explicando como funcionam as consultas recursivas antes de continuar. Encontre os Subordinados Diretos e Indiretos de Cada Gerente Na tabela funcionario, temos uma funcionária chamada Natália Jardim. Ela tem apenas um superior, o presidente da empresa. Natália é uma executiva em um cargo alto; naturalmente, ela tem subordinados. Seus subordinados também têm seus próprios subordinados. O que eu quero mostrar é como você consegue obter todos os subordinados, diretos e indiretos, de um gerente - nesse caso, Natália Jardim. Vou fazer isso usando um CTE recursivo. O código que vai me dar o que eu quero é o seguinte: WITH RECURSIVE subordinado AS ( SELECT funcionario_id, nome, sobrenome, gerente_id, 0 AS nivel FROM funcionario WHERE funcionario_id = 4529 UNION ALL SELECT e.funcionario_id, e.nome, e.sobrenome, e.gerente_id, nivel + 1 FROM funcionario e JOIN subordinado s ON e.gerente_id = s.funcionario_id ) SELECT s.funcionario_id, s.nome AS nome_subordinado, s.sobrenome AS sobrenome_subordinado, m.funcionario_id AS superior_direto_id, m.nome AS nome_superior_direto, m.sobrenome AS sobrenome_superior_direto, s.nivel FROM subordinado s JOIN funcionario m ON s.gerente_id = m.funcionario_id ORDER BY nivel; Na sintaxe SQL padrão, se sua intenção é ter um CTE recursivo, você começa o código escrevendo WITH RECURSIVE. Aviso importante! Se você estiver usando o SQL Server, isto não vai funcionar. Você precisará escrever a consulta acima sem RECURSIVE. Em outras palavras, a primeira linha do código deverá parecer com esta: WITH subordinado AS ( ... Agora, vamos voltar para a explicação do que o código acima faz. Ele cria um CTE recursivo chamado subordinado. O primeiro comando SELECT neste CTE retornará as colunas da tabela funcionario. Acrescentei também uma nova coluna, nivel. Natália Jardim será a gerente de nível 0; você entenderá o propósito desta coluna daqui a pouco. Como o ID de funcionário de Natália Jardim é 4529, acrescentei esse ID na cláusula WHERE. Quero que este resultado do comando SELECT seja "fundido" com o resultado do segundo comando SELECT. Para fazer isso, os dois comandos SELECT têm que ter o mesmo número de colunas no resultado. Para que a UNION ALL faça sentido, colocarei as colunas funcionario_id, nome, sobrenome e gerente_id no segundo SELECT. A última coluna do comando será o valor de nivel do primeiro comando SELECT (que é igual 0). A cada repetição, adicionaremos 1 a este valor, o que retornará os níveis de hierarquia. Isto nos ajudará a ordenar os dados e a saber facilmente quem é gerente de quem. Juntei a tabela funcionario com o próprio CTE. Estou tratando o CTE como uma tabela (e, de fato, é uma tabela), dando-lhe um pseudônimo e juntando as duas tabelas em e.gerente_id = s.funcionario_id. Finalmente, chego ao comando SELECT fora do CTE. Nesta parte do código, eu juntei o próprio CTE com a tabela funcionario. Primeiro, selecionamos as colunas funcionario_id, nome e sobrenome do CTE, pois estou usando o CTE como fonte dos dados subordinados. Também renomeei essas colunas assim, para evitar confusão. O próximo passo é selecionar as mesmas colunas da tabela funcionario. Estas colunas conterão os dados dos gerentes diretos dos funcionários. Finalmente, o resultado é ordenado pela coluna nivel, que ordenará os funcionários hierarquicamente. Uma consulta tão longa assim com certeza trará um resultado bem legal; confira: funcionario_idnome_subordinadosobrenome_subordinadosuperior_direto_idnome_superior_diretosobrenome_superior_diretonivel 4529NatáliaJardim4125MelissaRamos0 4500JoaquimHolanda4529NatáliaJardim1 4952SandraRibeiro4529NatáliaJardim1 4118MariaAssis4952SandraRibeiro2 4012JonasNogueira4952SandraRibeiro2 A tabela nos mostra todos os subordinados diretos e indiretos de Natália Jardim junto com seus superiores diretos. Examine a tabela e perceba que Natália Jardim tem dois subordinados diretos: Joaquim Holanda e Sandra Ribeiro. Joaquim não tem subordinados, mas Sandra tem dois: Maria Assis e Jonas Nogueira. Viu? Uma tarefa aparentemente simples pode não ser tão simples assim. Felizmente, as consultas recursivas são muito úteis nesses momentos. Tags: sql aprender sql consultas recursivas