Выберите COUNT дней между двумя датами, кроме выходных


9

Я пытаюсь определить количество дней между двумя разными датами, кроме выходных.

Я не знаю, как этого добиться.


1
Пожалуйста, опубликуйте структуру таблицы для справки. Выходные означает SAT и SUN?
Абдул Манаф

Ответы:


10

Предполагая, что под «выходными» вы подразумеваете субботу и воскресенье , это может быть еще проще:

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;
  • Вам не нужен дополнительный уровень подзапроса для generate_series(). SRF (набор возвращающих функций), также называемый «табличными функциями», может использоваться точно так же, как таблицы в FROMпредложении.

  • В частности, обратите внимание, что generate_series() включает верхнюю границу в выходных данных, если подходит полный интервал (3-й параметр). Верхняя граница исключается только в том случае, если последний интервал будет усеченным, что не относится к полным дням.

  • В соответствии с шаблоном ISODOWEXTRACT () , воскресенья представляются в 7соответствии со стандартом ISO. Позволяет для более простого WHEREусловия.

  • Скорее звоните generate_series()с timestampвводом. Вот почему:

  • count(*)немного короче и быстрее чем count(the_day), делая то же самое в этом случае.

Чтобы исключить нижнюю и / или верхнюю границу, добавьте / вычтите 1 день соответственно. Как правило, вы можете включить нижнюю и исключить верхнюю границу:

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;

3

В этом примере перечислены все даты между 2013-12-15 и 2014-01-02 (включительно). Во втором столбце указан день недели (численно от 0 до 6). В третьем столбце указано, является ли день недели субботой / воскресеньем или нет (вам придется адаптировать то, что вы считаете выходными), и это то, что можно использовать для подсчета дней недели.

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
;

3

Предполагая, что выходные - это суббота и воскресенье, вы можете использовать следующий 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)

Замените даты вашим выбором, а (0,6) - днями недели, которые вы хотите исключить.

Несколько вещей, которые вы должны принять к сведению: -

  1. Вы не упомянули, какую версию PostgreSQL вы используете. Это работает на 9.1+, но должно работать на более низких версиях.

  2. Выбранные даты включаются при использовании generate_series. Так что, если вы хотите промежуточные дни, добавьте 1 день к каждой дате.


0

Есть несколько вещей, которые вы можете сделать, чтобы сделать это проще. Метод, который я бы использовал, был бы для проверки наличия таблицы дат. Вы можете быстро создать такой как:

CREATE TABLE [dbo].[Dates]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY(0,1),
[Date] Date NOT NULL unique,
isWeekend BIT NOT NULL DEFAULT(0)
)

Как только таблица будет создана, вы сможете сравнительно быстро заполнить ее данными о дате.

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

Затем вы можете написать свой запрос в виде быстрого подсчета записей из этой таблицы.

select count(*) as NumberOfWeekDays
from dbo.dates 
where isWeekend = 0
and date between '1 Jan 2013' and '31 Dec 2013'

0

Я предлагаю вам создать функцию для использования в любое время и писать меньше; )

Этот код выше создаст функцию sql, которая считает и возвращает количество выходных дней (сб, вс). Просто у вас будет больше возможностей использовать эту функцию.

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;

После этого вы можете использовать функцию для возврата только количества выходных дней в интервале. Вот пример для использования:

SELECT <YourSchemaNameOptional>.count_full_weekend_days('2017-09-11', '2017-09-25') as days; --> RES: 4

Этот выбор должен вернуть четыре, потому что первый и второй день - понедельник, и у нас есть 2 субботы и 2 воскресенья между ними.

Теперь, чтобы вернуть только рабочие дни (без выходных), как вы хотите , просто сделайте вычитание, как в примере ниже:

SELECT (date '2017-09-25' - date '2017-09-11' ) - <YourSchemaName>.count_full_weekend_days('2017-09-11', '2017-09-25'); --> RES: 14 - 4 = 10

-2
-- 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)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.