Estou tentando obter o número de dias entre duas datas diferentes, exceto nos finais de semana.
Não sei como alcançar esse resultado.
Estou tentando obter o número de dias entre duas datas diferentes, exceto nos finais de semana.
Não sei como alcançar esse resultado.
Respostas:
Supondo que por "fim de semana" você queira dizer sábado e domingo , isso pode ser ainda mais simples:
SELECT count(*) AS count_days_no_weekend
FROM generate_series(timestamp '2014-01-01'
, timestamp '2014-01-10'
, interval '1 day') the_day
WHERE extract('ISODOW' FROM the_day) < 6;
Você não precisa de um nível de subconsulta extra para generate_series()
. SRF (funções de retorno de conjunto), também conhecidas como "funções de tabela", podem ser usadas como as tabelas da FROM
cláusula.
Observe em particular que generate_series()
inclui o limite superior na saída, desde que um intervalo completo (terceiro parâmetro) seja adequado. O limite superior será excluído apenas se o último intervalo for truncado, o que não acontece com dias inteiros.
Com o padrão ISODOW
para EXTRACT () , os domingos são relatados como 7
, de acordo com o padrão ISO. Permite uma WHERE
condição mais simples .
Em vez disso, ligue generate_series()
com a timestamp
entrada. Aqui está o porquê:
count(*)
é um pouco mais curto e mais rápido do que count(the_day)
, fazendo o mesmo neste caso.
Para excluir o limite inferior e / ou superior, adicione / subtraia 1 dia de acordo. Normalmente, você pode incluir o limite inferior e excluir o limite superior:
SELECT count(*) AS count_days_no_weekend
FROM generate_series(timestamp '2014-01-01'
, timestamp '2014-01-10' - interval '1 day'
, interval '1 day') the_day
WHERE extract('ISODOW' FROM the_day) < 6;
Este exemplo enumera todas as datas entre 15/12/2013 e 02-01-2014 (inclusive). A segunda coluna fornece o dia da semana (numericamente, entre 0 e 6). A terceira coluna marca se o dia da semana é um sábado / domingo ou não (você precisará adaptar o que considera um fim de semana) e é o que poderia ser usado para contar os dias da semana.
select '2013-12-15'::date + i * interval '1 day',
extract('dow' from '2013-12-15'::date + i * interval '1 day') as dow,
case when extract('dow' from '2013-12-15'::date + i * interval '1 day') in (0, 6)
then false
else true end as is_weekday
from generate_series(0, '2014-01-02'::date - '2013-12-15'::date) i
;
Supondo que um final de semana seja sábado e domingo, você pode usar o seguinte SQL.
select count(the_day) from
(select generate_series('2014-01-01'::date, '2014-01-10'::date, '1 day') as the_day) days
where extract('dow' from the_day) not in (0,6)
Substitua as datas por suas escolhas e (0,6) pelos dias da semana que você deseja excluir.
Algumas coisas que você precisa observar: -
Você não mencionou qual versão do PostgreSQL você está executando. Isso funciona no 9.1+, mas deve funcionar nas versões inferiores.
As datas escolhidas são inclusivas ao usar generate_series. Então, se você quiser dias entre, adicione 1 dia para cada data.
Existem algumas coisas que você pode fazer para facilitar isso. O método que eu usaria seria garantir que uma tabela de datas estivesse disponível. Você pode criar rapidamente um assim:
CREATE TABLE [dbo].[Dates]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY(0,1),
[Date] Date NOT NULL unique,
isWeekend BIT NOT NULL DEFAULT(0)
)
Depois que a tabela for criada, você poderá preenchê-la com dados de data relativamente rapidamente.
set datefirst 6 --start date is saturday
INSERT INTO dbo.Dates(Date, isWeekend)
select
Date,
case datepart(weekday,date)
--relies on DateFirst being set to 6
when 2 then 1
when 1 then 1
else 0
end as isWeekend
from (
select
dateadd(day, number - 1, 0) as date
from (
SELECT top 100000 row_number() over (order by o.object_id) as number
FROM sys.objects o
cross join sys.objects b
cross join sys.objects c
)numbers
)data
Você pode escrever sua consulta como uma contagem rápida de registros desta tabela.
select count(*) as NumberOfWeekDays
from dbo.dates
where isWeekend = 0
and date between '1 Jan 2013' and '31 Dec 2013'
Eu sugiro que você crie uma função para uso sempre que quiser e escreva menos; )
Este código acima criará uma função sql que conta e retorna a quantidade de dias de final de semana (sábado, domingo). Da mesma maneira que você terá mais flexibilidade para usar esta função.
CREATE OR REPLACE FUNCTION <YourSchemaNameOptional>.count_full_weekend_days(date, date)
RETURNS bigint AS
$BODY$
select COUNT(MySerie.*) as Qtde
from (select CURRENT_DATE + i as Date, EXTRACT(DOW FROM CURRENT_DATE + i) as DiaDate
from generate_series(date ($1) - CURRENT_DATE, date ($2) - CURRENT_DATE ) i) as MySerie
WHERE MySerie.DiaDate in (6,0);
$BODY$
LANGUAGE 'SQL' IMMUTABLE STRICT;
Depois disso, você pode usar a função para retornar apenas o número de dias do final de semana em um intervalo. Aqui está o exemplo a ser usado:
SELECT <YourSchemaNameOptional>.count_full_weekend_days('2017-09-11', '2017-09-25') as days; --> RES: 4
Essa seleção deve retornar quatro, porque o primeiro e o segundo dia são segunda-feira e temos dois sábados e dois domingos entre eles.
Agora, para retornar apenas dias úteis (sem fins de semana), como desejar , basta fazer uma subtração, como no exemplo abaixo:
SELECT (date '2017-09-25' - date '2017-09-11' ) - <YourSchemaName>.count_full_weekend_days('2017-09-11', '2017-09-25'); --> RES: 14 - 4 = 10
-- Returns number of weekdays between two dates
SELECT count(*) as "numbers of days”
FROM generate_series(0, (‘from_date’::date - 'todate'::date)) i
WHERE date_part('dow', 'todate'::date + i) NOT IN (0,6)
-- Returns number of weekdays between two dates
SELECT count(*) as days
FROM generate_series(0, ('2014/04/30'::date - '2014/04/01'::date)) i
WHERE date_part('dow', '2014/04/01'::date + i) NOT IN (0,6)