Adicionando dias úteis usando T-SQL
Introdução
Este artigo apresenta função para determinar uma data futura, apenas considerando os próximos dias úteis. Determinar um dia útil futuro é processo muito comum em empresas de prestação de serviços, como entregas de mercadorias, confecção de produtos perecíveis e outros.
Vou demonstrar um exemplo simples para ajudar quem tenha uma necessidade semelhante à descrita acima e também para que inspire quem possa precisar adicionar maiores detalhes para precisão se necessário, como indicar horas, minutos e segundos.
Sinta-se livre para adicionar ou modificar o conteúdo deste artigo.
Definindo quais são os dias úteis
Em cada país e até mesmo em cada segmento de trabalho podemos ter diferentes dias de início de semana. Para que possamos compreender melhor nossa configuração e como o SQL Server vai trabalhar com os dias da semana, vamos indicar no calendário abaixo o dia de útil informado pelo nosso usuário nesta demonstração (Quarta-feira, 27/8/2014) e nossos dias de folga, determinados para esta demonstração (Sábado, 30/8/2014 e Domingo, 31/8/2014).
Veja a imagem abaixo
Por padrão, o SQL Server define como início de semana o Domingo (weekday=7) no formato do idioma "us_english". Esta informação nós podemos obter através da variável global @@DATEFIRST, mas é importante notar que o dia indicado na configuração SET DATEFIRST é apenas uma referência para o parâmetro "weekday" utilizado e melhor explicado nos argumentos do método DATEPART.
No script T-SQL abaixo vamos alterar a data de início da semana apenas mudando o idioma do contexto atual do SQL Server. Nesta demonstração, vamos comparar os idiomas "Português Brasileiro", o "Inglês Britânico" e o "Inglês Americano" (padrão), utilizando a data de "31/8/2014" (Domingo).
Veja a imagem abaixo (clique na imagem para maximizar)
Veja abaixo este script T-SQL
DECLARE @SampleDate AS DATE = '2014-08-31';
SET LANGUAGE Brazilian; -- Brazilian
SELECT 'Brazilian' AS CurrentLanguage, @@DATEFIRST AS SetDateFirstSQL, DATENAME(weekday,@SampleDate) AS WeekDayName,
DATEPART(weekday,@SampleDate) AS WeekDayNumber;
SET LANGUAGE British; -- English - United Kingdom
SELECT 'English -UK' AS CurrentLanguage, @@DATEFIRST AS SetDateFirstSQL, DATENAME(weekday,@SampleDate) AS WeekDayName,
DATEPART(weekday,@SampleDate) AS WeekDayNumber;
SET LANGUAGE us_english; -- English - United States
SELECT 'English - USA' AS CurrentLanguage, @@DATEFIRST AS SetDateFirstSQL, DATENAME(weekday,@SampleDate) AS WeekDayName,
DATEPART(weekday,@SampleDate) AS WeekDayNumber;
GO
Nós podemos observar que, ao alterar o dia de início da semana baseado em padrão cultural de um país, modifica a configuração DATEFIRST e consequentemente o "weekday" de cada dia da semana. Agora, se utilizarmos a configuração DATEFIRST, este comando para ser majoritário em relação ao comportamento do SQL Server e sua relação aos dias da semana. Deste modo, o script T-SQL abaixo modifica o padrão do SQL Server e todas as consultas passarão a possuir um mesmo identificador para o dia da semana.
Veja a imagem abaixo (clique na imagem para maximizar)
Veja abaixo este script T-SQL
DECLARE @SampleDate AS DATE = '2014-08-31';
SET DATEFIRST 2;
SET LANGUAGE Brazilian; -- Brazilian
SELECT 'Brazilian' AS CurrentLanguage, @@DATEFIRST AS SetDateFirstSQL, DATENAME(weekday,@SampleDate) AS WeekDayName, DATEPART(weekday,@SampleDate) AS WeekDayNumber;
SET LANGUAGE British; -- English - United Kingdom
SELECT 'English - UK' AS CurrentLanguage, @@DATEFIRST AS SetDateFirstSQL, DATENAME(weekday,@SampleDate) AS WeekDayName, DATEPART(weekday,@SampleDate) AS WeekDayNumber;
SET LANGUAGE us_english; -- English - United States
SELECT 'English - USA' AS CurrentLanguage, @@DATEFIRST AS SetDateFirstSQL, DATENAME(weekday,@SampleDate) AS WeekDayName, DATEPART(weekday,@SampleDate) AS WeekDayNumber;
GO
Nós podemos observar que alterar o dia de início de semana através da configuração SET DATEFIRST para uso de um segmento de trabalho passa a ser o novo padrão no contexto utilizado pelo SQL Server, independentemente do idioma utilizado.
Na demonstração a seguir, estaremos utilizando o padrão do SQL Server (que é o mesmo utilizado pelo idioma "Português Brasileiro"), como segue na tabela abaixo:
Dia da Semana | DATEFIRST | WeekDay SQL (padrão) |
Segunda | 1 | 2 |
Terça | 2 | 3 |
Quarta | 3 | 4 |
Quinta | 4 | 5 |
Sexta | 5 | 6 |
Sábado | 6 | 7 |
Domingo | 7 | 1 |
Considerando que os "Dias de Folga" utilizados para esta demonstração são Sábado e Domingo, então utilizaremos a exclusão das datas respectivas aos "WeekDay" 7 e 1.
Criando a Função
A estruturação desta função pretende descartar os dias que não haverá expediente de trabalho para produção ou encaminhamento de um serviço solicitado. Deste modo, vamos utilizar os métodos DATEADD e DATEPART para adicionar e comparar a data prevista para o próximo dia útil, descartando os dias da semana: "sábado" (weekday = 7) e "domingo" (weekday = 1).
É possível também descartar os feriados nacionais, regionais e/ou municipais mas, apesar deste não ser o escopo principal deste artigo, podemos adicionar uma tabela com a descrição e a data do feriado, no formato utilizado pela função (neste caso o tipo de dados date) e assim descartar estas datas também de forma simples e prática.
Como uma forma de tratamento de erros, incluímos verificação para o parâmetro @DATE, e caso seu valor seja igual a NULL então a função entende que deve ser utilizada a data atual, utilizando a função GETDATE.
Um loop sobre o contador @COUNT compara se a data obtida pela variável @NEWDATE é a data correta indicada pelo parâmetro @NDAYS e que corresponde aos dias úteis esperados.
Veja abaixo este script T-SQL
CREATE FUNCTION dbo.ufn_ADD_WORKING_DAYS (
@DATE DATE,
@NDAYS INT
) RETURNS DATE
BEGIN
IF @DATE IS NULL
BEGIN
SET @DATE = GETDATE();
END
DECLARE @STARTDATE INT = 0
DECLARE @COUNT INT = 0
DECLARE @NEWDATE DATE = DATEADD(DAY, 1, @DATE)
WHILE @COUNT < @NDAYS
BEGIN
IF DATEPART(WEEKDAY, @NEWDATE) NOT IN (7,1) --AND @NEWDATE NOT IN ( SELECT DT_HOLIDAY FROM TB_HOLIDAYS )
SET @COUNT += 1;
SELECT @NEWDATE = DATEADD(DAY, 1, @NEWDATE), @STARTDATE += 1;
END
RETURN DATEADD(DAY, @STARTDATE, @DATE);
END
GO
Nota |
---|
O dia de início da semana(DATEFIRST) que utilizamos nesta demonstração é o padrão do SQL Server. Você poderá alterar o script T-SQL desta função para descartar outros dias de folga, como utilizado na condição IN. |
Utilizando a Função
Veja abaixo um exemplo da utilização desta função, onde convertemos uma data do formato varchar para date e adicionamos 4 dias úteis.
Então, como a data indicada é 27/08/2014 (quarta-feira) e considerando que estamos descartando os dias 30/08/2014(sábado) e 31/08/2014(domingo), passamos a ter como resultado o dia útil 02/09/2014.
**Veja a imagem abaixo
**
Veja abaixo este script T-SQL
SELECT dbo.ufn_ADD_WORKING_DAYS( CAST('2014-08-27' AS DATE), 4 )
GO
Este exemplo também pode ser utilizado em uma instrução SELECT para consultar diversos dados de uma tabela, referenciando como parâmetro desta função uma coluna do tipo date.
Conclusão
É importante identificar e formatar os dados que recebemos para melhorar às condições de armazenamento e exibição de dados.
Propor um prazo para conclusão de um trabalho com maior precisão é um dos melhores critérios para diferenciar sua empresa no mercado.
Referências
Veja Também
- How To Find Various Day, Current Week, Two Week, Month, Quarter, Half Year and Year In SQL Server
- T-SQL: Calendar Table
- Transact-SQL Portal