Soluções da Delegacia SQL

Inquérito 007, "Litro por litro": solução passo a passo

O boletim

Não é crime da cidade: é crime de dentro de casa.

A delegacia abastece a viatura no Posto Trentini por convênio com a prefeitura. O posto manda a nota, a prefeitura paga e ninguém confere. De janeiro a junho a conta passou de cinco mil e oitocentos reais, quase o dobro do ano passado, e a prefeitura mandou um ofício perguntando se a delegacia comprou um segundo carro.

O doutor abriu sindicância interna, protocolo trezentos e noventa e dois, e quer três coisas até o fim do dia: quanto cada um abasteceu, em que veículos esse combustível entrou, e — já que a chefia nova sabe mexer na máquina — quantas vezes cada login abriu o SISPOL, porque ele desconfia que tem gente que não trabalha.

A viatura é a Ipanema branca, placa AIU-7001, o `id` 1 da tabela `veiculo`. Seu Jaime avisa: aqui não vai adiantar ler linha por linha. São cento e quarenta notas no livro do convênio, a tabela `abastecimento`, setenta e quatro só de janeiro a junho. Aqui a pergunta é de quanto, não de qual, e para responder quanto existe um jeito de a máquina somar sozinha.

Cinco nomes deram depoimento à sindicância, o do doutor entre eles. A chefia quer um nome, e quer a conta que mostra o nome.

O que o caso ensina: COUNT, SUM e GROUP BY.

Pergunta 1

O ofício da prefeitura quer a conta do semestre. Traga quantas notas o convênio teve de janeiro a junho de 2003 e quanto elas custaram, em reais.

Ver as dicas do jogo
  1. O ofício da prefeitura fala da conta do semestre. Traga, da tabela `abastecimento`, a contagem de notas e a soma do `valor` de todas as notas com `data` de 1º de janeiro a 30 de junho de 2003.
  2. É uma linha só: COUNT(*) conta as notas e SUM(`valor`) soma o dinheiro, e o semestre é a `data` entre '2003-01-01' e '2003-06-30', com BETWEEN.
  3. Escreva: `SELECT COUNT(*), SUM(valor) FROM abastecimento WHERE data BETWEEN '2003-01-01' AND '2003-06-30';`
Ver a consulta que resolve
SELECT COUNT(*), SUM(valor) FROM abastecimento WHERE data BETWEEN '2003-01-01' AND '2003-06-30';

`COUNT(*)` e `SUM(coluna)` são funções de agregação: a primeira conta quantas linhas passaram pelo `WHERE`, a segunda soma os números de uma coluna dessas linhas. Sem mais nada, todas as notas do semestre viram uma pilha só, e o resultado é uma linha.

Resultado

quantas notas quantos reais
74 5847

Pergunta 2

Agora a mesma conta, assinatura por assinatura: quantas notas e quantos litros cada um que assinou tem no semestre.

Ver as dicas do jogo
  1. Agora a mesma pilha de notas, pessoa por pessoa. Traga, da tabela `abastecimento`, de 1º de janeiro a 30 de junho de 2003, o `pessoa_id` de quem assinou, a contagem de notas e a soma dos `litros` de cada um.
  2. Uma pilha por assinatura é GROUP BY `pessoa_id`; o COUNT(*) e o SUM(`litros`) passam a contar e somar cada pilha.
  3. Escreva: `SELECT pessoa_id, COUNT(*), SUM(litros) FROM abastecimento WHERE data BETWEEN '2003-01-01' AND '2003-06-30' GROUP BY pessoa_id;`
Ver a consulta que resolve
SELECT pessoa_id, COUNT(*), SUM(litros) FROM abastecimento WHERE data BETWEEN '2003-01-01' AND '2003-06-30' GROUP BY pessoa_id;

`GROUP BY` é quem faz o grupo existir: ele junta numa pilha só todo mundo que tem o mesmo `pessoa_id`, e o `COUNT` e o `SUM` passam a contar e somar pilha por pilha. Sem ele, a consulta volta a ser a da prefeitura: uma linha só.

Resultado

pessoa_id quantas notas quantos litros
2 5 190
4 39 1410
5 28 1120
6 2 132

Pergunta 3

O doutor quer saber quem trabalha nesta casa. Conte, no livro de acesso inteiro, quantas vezes cada login fez cada operação.

Ver as dicas do jogo
  1. O doutor também pediu o livro de acesso do SISPOL, a tabela `acesso`. Traga o `usuario_id`, a `operacao` e quantas vezes cada combinação das duas aparece no livro inteiro.
  2. Duas colunas no GROUP BY, `usuario_id` e `operacao`, e COUNT(*) ao lado. Sem faixa de datas.
  3. Escreva: `SELECT usuario_id, operacao, COUNT(*) FROM acesso GROUP BY usuario_id, operacao;`
Ver a consulta que resolve
SELECT usuario_id, operacao, COUNT(*) FROM acesso GROUP BY usuario_id, operacao;

`GROUP BY` aceita mais de uma coluna: aqui ele junta por `usuario_id` e por `operacao` ao mesmo tempo, então cada linha do resultado é uma combinação das duas, e o mesmo login aparece uma vez para cada operação diferente que ele fez.

Resultado

usuario_id operacao quantas vezes
11 consulta 3
12 baixa 13
12 consulta 56
12 gravacao 46
12 impressao 44
13 consulta 255
13 gravacao 270
13 impressao 278
14 consulta 132
14 gravacao 145
15 consulta 136
17 consulta 4

Pergunta 4

Separe a conta do semestre por carro e por assinatura ao mesmo tempo: quantas notas e quantos litros cada par tem.

Ver as dicas do jogo
  1. Separe as notas do semestre por carro e por pessoa ao mesmo tempo. Traga, da tabela `abastecimento`, de 1º de janeiro a 30 de junho de 2003, o `veiculo_id`, o `pessoa_id`, a contagem de notas e a soma dos `litros` de cada par.
  2. GROUP BY com as duas colunas, `veiculo_id` e `pessoa_id`, na mesma faixa de datas das outras contas.
  3. Escreva: `SELECT veiculo_id, pessoa_id, COUNT(*), SUM(litros) FROM abastecimento WHERE data BETWEEN '2003-01-01' AND '2003-06-30' GROUP BY veiculo_id, pessoa_id;`
Ver a consulta que resolve
SELECT veiculo_id, pessoa_id, COUNT(*), SUM(litros) FROM abastecimento WHERE data BETWEEN '2003-01-01' AND '2003-06-30' GROUP BY veiculo_id, pessoa_id;

É o mesmo `GROUP BY` de duas colunas do livro de acesso, agora no livro do convênio. Cada linha é uma combinação que existe de verdade nas notas: se um par não aparece, é porque nenhuma nota tem aquele carro com aquela assinatura.

Resultado

veiculo_id pessoa_id quantas notas quantos litros
1 2 5 190
1 4 21 798
1 5 28 1120
1 6 2 132
33 4 18 612

Pergunta 5

Traga a ficha de cada carro que aparece nas notas do semestre.

Ver as dicas do jogo
  1. Traga, da tabela `veiculo`, o `id`, a `placa`, o `modelo`, a `cor`, o `ano` e o `dono_id` de cada veículo que aparece nas notas do semestre.
  2. Os números dos carros estão na conta por carro e por pessoa. Ponha os dois numa lista, depois de IN, para a coluna `id`.
  3. Escreva: `SELECT id, placa, modelo, cor, ano, dono_id FROM veiculo WHERE id IN (1, 33);`
Ver a consulta que resolve
SELECT id, placa, modelo, cor, ano, dono_id FROM veiculo WHERE id IN (1, 33);

O `IN` do caso 4 continua pedindo uma lista de valores de uma vez só. A lista quem escreveu foi o chefe, lendo o resultado de antes: a máquina não sabe quais carros importam, o chefe sabe.

Resultado

id placa modelo cor ano dono_id
1 AIU-7001 Chevrolet Ipanema (viatura) branca 1996 0
33 AJR-0182 Chevrolet Monza cinza 1991 78

Quem foi

Ver o culpado e a prova

O culpado é Valdemar Prestes.

As provas que fecham o caso:

  • A conta por carro e por pessoa: dezoito notas assinadas pelo 4 no carro 33, que não é a viatura.
  • Dezoito notas assinadas pelo 4 no carro 33, e o carro 33 é um Monza cinza no nome de outra pessoa.
  • O 4 aparece duas vezes na conta por carro e por pessoa: vinte e uma notas na viatura, o carro 1, e dezoito no carro 33.

O doutor leu a tela em silêncio e mandou chamar o Cabeção. O Cabeção entrou fazendo piada e parou de fazer piada quando viu os seiscentos e doze litros somados em números grandes.

Disse que o Monza era da sogra. Disse que ia devolver. Disse que era o adicional noturno que cortaram em 2002. Disse que todo mundo faz. Nessa última o doutor levantou a cabeça e perguntou quem é todo mundo, e o Cabeção não respondeu, porque ele mente por preguiça e não por coragem.

Foi afastado com sindicância, e o delegado mandou pôr cadeado na bomba do convênio.

À noite, com a delegacia vazia, Seu Jaime pediu para você abrir de novo a conta dos logins. Ficou muito tempo na linha da baixa. Disse que o usuário doze é o doutor, desde que o doutor chegou. Depois perguntou, sem olhar para você, quem é que fica aqui depois que ele vai embora. Do balcão vinha o barulho da máquina de escrever.