Calculando dias úteis com SQL Server
Neste artigo você vai aprender como calcular dias úteis utilizando o SQL Server.
O cálculo de dias úteis é muito importante e tem várias aplicações, como por exemplo o cálculo de prazos de entrega e de pagamentos.
Vamos considerar como dia útil uma data que seja entre segunda a sexta, e que não caia em algum feriado.
Para servir de referência, vamos primeiro criar uma tabela com os feriados nacionais de 2020.
CREATE TABLE [dbo].[feriados](
[id] [int] IDENTITY(1,1) NOT NULL,
[data] [date] NULL,
[descricao] [varchar](50) NULL,
CONSTRAINT [PK_feriados] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Depois de criar a tabela, vamos inserir os registros com os feriados de 2020.
INSERT INTO feriados (data, descricao) VALUES ('2020-01-01', 'Confraternização Universal')
INSERT INTO feriados (data, descricao) VALUES ('2020-04-10', 'Paixão de Cristo')
INSERT INTO feriados (data, descricao) VALUES ('2020-04-21', 'Tiradentes')
INSERT INTO feriados (data, descricao) VALUES ('2020-05-01', 'Dia Mundial do Trabalho')
INSERT INTO feriados (data, descricao) VALUES ('2020-09-07', 'Independência do Brasil')
INSERT INTO feriados (data, descricao) VALUES ('2020-10-12', 'Nossa Senhora Aparecida')
INSERT INTO feriados (data, descricao) VALUES ('2020-11-02', 'Finados')
INSERT INTO feriados (data, descricao) VALUES ('2020-11-15', 'Proclamação da República')
INSERT INTO feriados (data, descricao) VALUES ('2020-12-25', 'Natal')
Para conferir, basta fazer:
SELECT * from feriados
Com a nossa tabela criada, vamos criar uma funçao para facilitar a geração de datas com base numa quantidade de dias úteis a ser informada pelo usuário. Antes de criar a função, vamos entrar em detalhes de como ela vai funcionar:
1 - A função vai receber dois parâmetros, a data inicial e a quantidade de dias úteis a ser calculada.
2 - Será feito um loop de acordo com a quantidade de dias solicitados
3 - A data inicial será incrementada em 1 dia antes e durante a execução do loop
4 - Para cada nova data, será verificado se a nova data está no intervalo de dia da semana válido, e se a data não está em nossa tabela de feriados
5 - Ao concluir o loop, será retornada a nova data.
Existem três pontos que vamos dar uma olhada antes de criar a função. O primeiro é: Como saber o dia da semana de acordo com a data?
Para isso, se utiliza a funçao DATEPART, ela recebe dois parâmetros, o tipo de retorno e a data a ser analisada. O tipo de retorno que será utilizado é o WEEKDAY.
select DATEPART(WEEKDAY, '2020-02-29')
O retorno desta data é 7 (sábado). O retorno vai ser um número de 1 a 7. Por padrão, a semana começa no domingo (1) e termina no sábado (7).
O outro ponto que vamos ver é como adicionar dias a uma data. Para isso, se utilizada a função DATEADD, ela recebe 3 parâmetros, o primeiro é a referência (dias, mês, ano, etc...), o segundo é um número que representa a quantidade que vai ser adicionada, e por último, a data. Se você informar a data de hoje e quiser saber a data de amanhã, basta fazer a seguinte consulta:
SELECT DATEADD(DAY, 1, GETDATE())
Por último, para fazer o loop de acordo com a quantidade de dias solicitados, será utilizada uma estrutura de repetição chamada "While", que vai executar certas instruções enquanto uma variável de referência for válida.
DECLARA @DIAS_UTEIS INT = 30
DECLARE @CONTADOR INT = 0
WHILE @CONTADOR < @DIAS_UTEIS
BEGIN
//Instrucoes que vamos executar
SET @CONTADOR += 1; //Incrementar o contador
END
No exemplo acima ele vai executar as instrucoes dentro do While enquanto a variável "contador" for menor que 30.
Agora vamos criar a nossa função. Ela vai chamar "adicionar_dias_uteis", e vai receber dois parâmetros, uma data e uma quantidade de dias úteis. Seu retorno vai ser uma data.
CREATE FUNCTION adicionar_dias_uteis (
@DATA DATE,
@DIAS_UTEIS INT
) RETURNS DATE
BEGIN
IF @DATA IS NULL
BEGIN
SET @DATA = GETDATE();
END
DECLARE @DIAS INT = 0
DECLARE @CONTADOR INT = 0
DECLARE @NOVA_DATA DATE = DATEADD(DAY, 1, @DATA)
WHILE @CONTADOR < @DIAS_UTEIS
BEGIN
IF DATEPART(WEEKDAY, @NOVA_DATA) NOT IN (7,1) AND @NOVA_DATA NOT IN ( SELECT data FROM feriados )
SET @CONTADOR += 1;
SELECT @NOVA_DATA = DATEADD(DAY, 1, @NOVA_DATA), @DIAS += 1;
END
RETURN DATEADD(DAY, @DIAS, @DATA);
END
GO
Chegou a hora de testar. Como exemplo, vamos calcular 90 dias úteis a partir de 10/03/2020.
SELECT dbo.adicionar_dias_uteis('2020-03-10', 90)
O resultado será
2020-07-16