Olá.
Estava fazendo alguns testes na procedure criada nesta aula, que inclui a reserva de um aluguel baseado na data inicial e número de dias, excluindo finais de semana (finais de semana são adicionados à reserva, mas não são cobrados, então não entram na contagem de número de dias).
Acabei encontrando dois bugs por conta da lógica implementada no loop:
- Se a reserva começar no sábado ou domingo, o primeiro dia é computado na contagem de número de dias (
vContador
está sendo iniciado como 1); - Se o último dia cair num sábado, a reserva não é atualizada para ir até segunda-feira (o loop já vai ter terminado sem verificar se o último dia é válido).
Segue abaixo uma possível correção do código, levando em conta essas condições. Outras partes do meu código estão um pouco diferentes das do instrutor, mas fazem a mesma coisa. O que eu fiz foi iniciar vContador
em 0 e vDataFim
como sendo um dia antes de vDataInicio
(para que a contagem comece a partir do primeiro dia dentro do loop corrigido). Dentro do loop, primeiro calculo vDataFim
e só depois verifico qual é o dia da semana em dayofweek
. Assim, se o primeiro dia for um sábado ou domingo, ele não é computado na contagem de dias, bem como se o último dia for no sábado.
DROP PROCEDURE IF EXISTS nova_reserva;
DELIMITER //
CREATE PROCEDURE nova_reserva(
vReserva VARCHAR(10),
vClienteNome VARCHAR(255),
vHospedagem VARCHAR(10),
vDataInicio DATE,
vDias INTEGER, -- incluindo vDataInicio
vPrecoUnitario DECIMAL(10,2) -- valor da diária
)
BEGIN
-- novas regras:
-- 1. calcular a data final com base na inicial e número de dias
-- 2. finais de semana NÃO contam para o cálculo dos dias e são
-- incluídos de graça no total da reserva
DECLARE vDataFim DATE;
DECLARE vContador Integer;
DECLARE vDiaSemana Integer;
-- a partir do nome, vamos encontrar o id consultando a tabela clientes
DECLARE vCliente VARCHAR(10);
-- vamos verificar se há mais de um cliente com o mesmo nome
DECLARE vQtdCliente INTEGER;
-- variável para cálculo do preço total da reserva
DECLARE vPrecoTotal DECIMAL(10,2);
-- variável para exibir mensagem de erro
DECLARE vMensagem VARCHAR(100);
-- tratamento de erro para chave estrangeira (erro 1452)
DECLARE EXIT HANDLER FOR 1452
BEGIN
SET vMensagem = 'Problema de chave estrangeira';
-- use o select para exibir a mensagem como resultado da consulta
SELECT vMensagem;
END;
-- verificando se há mais de um cliente com o mesmo nome
-- podemos usar o resultado do SELECT como valor da variável
SET vQtdCliente = (SELECT count(*) FROM clientes WHERE nome = vClienteNome);
CASE
WHEN vQtdCliente > 1 THEN
SET vMensagem = 'Não é possível fazer a inclusão, pois há mais de um cliente com o mesmo nome.';
SELECT vMensagem;
WHEN vQtdCliente = 0 THEN
SET vMensagem = 'Não é possível fazer a inclusão, pois o cliente não existe.';
SELECT vMensagem;
WHEN vQtdCliente = 1 THEN
-- cálculo dos dias, aplicando as novas regras
SET vContador = 0;
-- a data de início será incluída como sendo o primeiro dia
-- das diárias. O loop faz esse acerto.
SET vDataFim = vDataInicio - INTERVAL 1 DAY;
WHILE vContador < vDias
DO
-- independente do dia, adicionamos à data final
SET vDataFim = vDataFim + INTERVAL 1 DAY;
SET vDiaSemana = dayofweek(vDataFim);
-- 1 é domingo e 7 é sábado
IF (vDiaSemana <> 1 AND vDiaSemana <> 7) THEN
SET vContador = vContador + 1;
END IF;
END WHILE;
-- cálculo do valor total da reserva
SET vPrecoTotal = vDias * vPrecoUnitario;
-- ao invés de um SET, podemos usar o SELECT INTO para
-- atribuir o valor a uma variável
SELECT cliente_id INTO vCliente FROM clientes WHERE nome = vClienteNome;
INSERT INTO reservas VALUES (
vReserva, vCliente, vHospedagem, vDataInicio, vDataFim, vPrecoTotal
);
SET vMensagem = 'Aluguel incluído na base com sucesso!';
SELECT vMensagem;
END CASE;
END//
DELIMITER ;