Questão de Entrevista | ||
País: Irlanda | Tipo: Técnica | Assunto: Banco de Dados |
Ramo de negócio da empresa: Financeiro (Banco) | Grau de Dificuldade: Média |
Data a tabela Employee, escreva consultas que retorne:
- O nome e o salário dos empregados que recebem mais do que os seus gerentes (manager) diretos, ordenando o resultado pelo salário de forma decrescente
- O nome dos empregados que possuem exatamente outros dois empregados com o mesmo dia e mês de aniversário.
Abaixo o script para criar e inserir os dados da tabela (padrão ANSI, pode ser usado em qualquer banco de dados)
-- cria a tabela Employee (Empregado) -- com chave primária nomeada como PK_Employee CREATE TABLE dbo.Employee( Id int NOT NULL, Name varchar(50) NOT NULL, Birthday datetime NOT NULL, Salary numeric(18, 2) NOT NULL, ManagerId int, CONSTRAINT PK_Employee PRIMARY KEY (Id) ) -- cria uma chave estrangeira (foreign key) -- relacionando o gerente (ManagerId) ao empregado (Id) ALTER TABLE dbo.Employee WITH CHECK ADD CONSTRAINT FK_Employee_Employee FOREIGN KEY(ManagerId) REFERENCES dbo.Employee (Id) --insere registros na table a Employee INSERT INTO Employee (Id,Name,Birthday,Salary,ManagerId) VALUES (1, 'John', '1978-02-22', 5000, null) -- como todos os campos estão sendo usados, podemos simplificar -- o comando ocultado p nome das colunas na instrução INSERT INSERT INTO Employee VALUES (2, 'Mary', '1983-08-25', 5200, 1) INSERT INTO Employee VALUES (3, 'Paul', '1994-11-07', 3750, 2) INSERT INTO Employee VALUES (4, 'Anna', '2001-12-18', 2450, 3) INSERT INTO Employee VALUES (5, 'Aoifa', '1994-11-07', 4200, 3) INSERT INTO Employee VALUES (6, 'Emily', '1995-03-05', 3200, 1) INSERT INTO Employee VALUES (7, 'Alan', '1997-05-26', 3800, 3) INSERT INTO Employee VALUES (8, 'Lucy', '2001-11-26', 3800, 2) INSERT INTO Employee VALUES (9, 'Adan', '1989-08-25', 4760, null) INSERT INTO Employee VALUES (10, 'Will', '2001-12-01', 2800, null) INSERT INTO Employee VALUES (11, 'Sophia', '2002-04-08', 2930, 10) INSERT INTO Employee VALUES (12, 'Emma', '1998-09-20', 2560, 10) INSERT INTO Employee VALUES (13, 'James', '1995-08-25', 2560, 9) INSERT INTO Employee VALUES (14, 'Charlotte', '1995-10-12', 2980, 9) INSERT INTO Employee VALUES (15, 'Lucas', '1986-01-11', 2900, 10)
Vamos a questão 1: retornar o nome e o salário dos empregados que recebem mais do que os seus gerentes diretos, ordenando os registros pelo salário de forma decrescente.
Analisando os dados nota-se que a coluna ManagerId é uma auto-referência para a tabela Employee, ou seja, indica o registro na tabela que se refere ao gerente da pessoa em questão. Por exemplo: John (Id = 1) não tem um gerente direto, pois seu ManagerId é nulo. Já Mary (Id= 2) é subordinada a John, pois seu ManagerId é igual 1, que é o Id de John. Mary tem um salário de $5200, enquanto John tem um salário de $5000, logo Mary deve ser listada.
Uma das formas de resolver este problema é usando um subselect e comparar o salário do cada registro da tabela Employee com o salário do seu respectivo gerente. Como quem não tem gerente direto não precisa ser listado, por questões de desempenho pode-se adicionar um filtro para ignorar estas pessoas, com isto o subselect não será executado nestes casos (o engine do banco de dados sabe que uma expressão com subselect é mais cara, computacionalmente falando, que uma expressão que usa apenas um campo da própia tabela, como o operador condicional AND (E) exige que as duas expressões sejam verdadeira para ser verdadeiro, ele executa primeiro a condição e.ManagerId is not null e ignora a segunda expressão se a primeira for falsa):
select Name, Salary from Employee e where e.ManagerId is not null and e.Salary > (select Salary from Employee e2 where e2.Id = e.ManagerId) order by e.Salary desc
Executando o comando acima:
Name | Salary |
Mary | 5200.00 |
Aoifa | 4200.00 |
Alan | 3800.00 |
Sophia | 2930.00 |
Lucas | 2900.00 |
Outra forma de resolver o problema é fazer um INNER JOIN da tabela Employee com ela mesma. Esta forma, além de ter melhor performance, permite listar o nome e o salaário do gerente, o que facilita a verificação do resultado (isto está fora do escopo do exercício inicial, se o exercício for corrigido eletronicamente, devolva exatamente o que se pede. Se for uma programação em par – entrevistador e entrevistado – você pode usar mas deve destacar o que está fazendo):
select e.Name, e.Salary, e2.Name Manager, e2.Salary from Employee e inner join (select Id, Name, Salary from Employee) e2 on e2.Id = e.ManagerId where e.Salary > e2.Salary order by e.Salary desc
Perceba que no exemplo acima a condição e.ManagerId is not null foi suprimida. Como foi usado um INNER JOIN, que obriga que o registro exista dos dois lados da junção, os registros com ManagerId = NULL são automaticamente descartados.
Name | Salary | Manager | Salary |
Mary | 5200.00 | John | 5000.00 |
Aoifa | 4200.00 | Paul | 3750.00 |
Alan | 3800.00 | Paul | 3750.00 |
Sophia | 2930.00 | Will | 2800.00 |
Lucas | 2900.00 | Will | 2800.00 |
Agora a questão 2: listar o nome nome dos empregados que possuem exatamente outros dois empregados com o mesmo dia e mês de aniversário.
Para responder a questão vamos usar a mesma base de comando da primeira opção de resposta da questão 1, com alterações na cláusula where da subconsulta. Para comprar os dias e meses foram usadas, respectivamente, as funções DAY e MONTH (estas funções são do SQL Server, consulte a documentação do seu banco de dados para encontrar a função equivalente).
select Name from Employee e where (select count(*) from Employee e2 where e2.Id <> e.Id and day(e2.Birthday) = day(e.Birthday) and month(e2.Birthday) = month(e.Birthday)) = 2
O resultado é:
Name |
Mary |
Adan |
James |
Note que Aoifa e Paul também fazem aniversário no mesmo dia e mês (07 de novembro), mas o exercício pede que sejam listadas apenas o empregados que possuem exatamente outros dois com o mesmo dia e mês de aniversário, por isto a inclusão da condição e2.Id <> e.Id na cláusula where da subconsulta.