Compartilhar via


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

Outros Idiomas