Поиск заказов, сделанных в течение любой недели ISO определенного года, с использованием T-SQL - PullRequest
3 голосов
/ 14 декабря 2010

В рамках настройки отчетности у меня есть SQL-запрос, извлекающий количество заказов, размещаемых каждую неделю:

select datepart(isowk, order_date), count(*)
from orders where year(order_date) = @Year
group by datepart(isowk, order_date), year(order_date)
order by 1

Обратите внимание, что я использую новый формат isowk в вызове функции datepart, так как в Дании обычно используются номера недели ISO.

Выполнение этого запроса с @Year = 2010 дает набор результатов, аналогичный следующему:

1           5
2           7
3           10
...
53          3

Мы оба знаем, что 2010 год еще не закончился, и, конечно, еще не было недели 53. На самом деле, первые три дня года принадлежали неделе 53 ИСО 2009 года. Хотя это можно объяснить другим программистам, люди, которые собираются читать мои отчеты, никогда не поймут этого. Следовательно, я хочу избавиться от этой недели 53, «перенеся» данные на 2009 год.

Как мне переписать мое предложение WHERE, чтобы отфильтровать набор данных по заказам, сделанным между понедельником недели 1 и пятницей последней недели (52 или 53) в году, определенном как @Year?

Ответы [ 5 ]

2 голосов
/ 14 декабря 2010

На самом деле я не знаю ни одного языка программирования, который бы предоставлял isoyear ...

create function isoyear(@date datetime) returns smallint as begin
declare @isoyear smallint =
    case
    when datepart(isowk, @date) = 1 and month(@date) = 12
    then year(@date)+1
    when datepart(isowk, @date) = 53 and month(@date) = 1
    then year(@date)-1
    else year(@date)
    end;
return @isoyear;
end;

Надеюсь, мы получим datepart (isoyear, ...) в следующий раз ...

1 голос
/ 14 декабря 2010

Вы можете попробовать это:

set datefirst 1;

declare @Year smallint = 2010;

declare @DayInFirstWeek datetime = cast(@Year as varchar)+'0104';
declare @FirstDayInFirstWeek datetime = 
             @DayInFirstWeek - datepart(dw,@DayInFirstWeek)+1
declare @DayInFirstWeekNextYear datetime = cast(@Year+1 as varchar)+'0104';
declare @LastDayInLastWeek datetime = 
             @DayInFirstWeekNextYear - datepart(dw,@DayInFirstWeekNextYear);

select datepart(isowk, order_date), count(*) 
from orders
where order_date between @FirstDayInFirstWeek and @LastDayInLastWeek
group by datepart(isowk, order_date)
order by 1;

Если ваш первый день недели не понедельник, возможно, он не сработает!

0 голосов
/ 18 марта 2013

Просто чтобы сформулировать фундаментальный ответ здесь:

Если вы запрашиваете, основываясь на неделях ISO, , вы должны фильтровать на основе года ISO (а не календарного года)

Как вы добавляете столбец года ISO в свои данные (или где пункт), как другие описали.

0 голосов
/ 14 декабря 2010

Я собираюсь попробовать это сам, используя подход, описанный в моих комментариях к ответ Манфреда . Идея состоит в том, чтобы найти первый день первой недели ISO указанного года и год после, что позволит включить индексирование between для входных записей.

Сначала я создаю функцию, которая будет находить первый день первой недели ISO в определенном году:

create function firstIsoDay(@year int) returns datetime as begin
    declare @first datetime
    select @first = cast(cast(@year as char(4)) as datetime)
    select @first = dateadd(ww, datediff(ww, 0, @first), 0)
    if datepart(isowk, @first) > 1 set @first = @first + 7
    return @first
end

Отказ от ответственности: Эта функция не проверена (на данный момент Sql Server 2008 недоступен).

Фактический запрос данных может быть выполнен с использованием двух вызовов этой функции в фильтре between:

select datepart(isowk, order_date), count(*) from orders
where order_date between firstIsoDay(@Year) and firstIsoDay(@Year + 1)
group by datepart(isowk, order_date), year(order_date)
order by 1

Помните, что between является двусторонним включением. Если значения в order_date содержат укороченные даты (без времени), в конец предложения where следует добавить -1. В моем случае, order_date имеет и дату, и время, поэтому между двумя датами в полночь все будет хорошо.

0 голосов
/ 14 декабря 2010

Сделайте оператор IF - я не гарантирую синтаксис сервера SQL, но:

// IF ISO week is 53, return week 1 of next year. 
GROUP BY IF(datepart(isowk, order_date)) = 53,
  CONCAT("1/", year(order_date) + 1),
  CONCAT(datepart(isowk, order_date), "/", year(order_date))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...