Всегда возвращать данные с понедельника по воскресенье на прошлой неделе - PullRequest
4 голосов
/ 23 августа 2011

Как написать SQL-оператор, который всегда возвращает данные с последнего понедельника до последнего воскресенья?Любое руководство приветствуется.

Спасибо.

Ответы [ 4 ]

13 голосов
/ 24 августа 2011

Ответ t-clausen.dk работает, но может быть неясно , почему работает (ни вам, ни разработчику, который придет за вами). Поскольку дополнительная ясность иногда достигается за счет краткости и производительности, я хотел бы объяснить, почему это работает, если вы предпочитаете использовать этот более короткий синтаксис.

SELECT t.*  
FROM <table> t CROSS JOIN 
(SELECT DATEDIFF(day, 0, getdate() - DATEDIFF(day, 0, getdate()) %7) lastmonday) a 
WHERE t.yourdate >= a.lastmonday - 7 and yourdate < a.lastmonday 

Как SQL Server хранит datetime Внутренне

SQL Server хранит datetime как два 4-байтовых целых числа; первые четыре байта представляют количество дней с 01.01.1900 (или до 01.01.1900 для отрицательных чисел), а вторые четыре байта представляют количество миллисекунд с полуночи.

Использование datetime с int или decimal

Поскольку datetime хранится в виде двух 4-байтовых целых чисел, в T-SQL легко перемещаться между типами данных числовых и дат. Например, SELECT GETDATE() + 1 возвращает то же, что и SELECT DATEADD(day, 1, GETDATE()), а CAST(40777.44281 AS datetime) совпадает с 2011-08-24 10:37:38.783.

1/1/1900

Поскольку первая целочисленная часть datetime представляет собой, как было упомянуто выше, количество дней с 01.01.1900 (также называемое эпохой SQL Server), CAST(0 as datetime) по определению эквивалентно 1900-01-01 00:00:00

DATEDIFF (день, 0, GETDATE ())

Здесь все становится сложнее и веселее. Во-первых, мы уже установили, что когда 0 обрабатывается как дата, оно совпадает с 01.01.1900, поэтому DATEDIFF(day, '1/1/1900', GETDATE()) совпадает с DATEDIFF(day, 0, GETDATE()) - оба вернут текущее количество дней с 1 / 1/1900. Но подождите: текущее количество дней - это именно то, что представлено первыми четырьмя байтами даты и времени! В одном утверждении мы сделали две вещи: 1) мы удалили часть «времени», возвращаемую GETDATE(), и у нас есть целочисленное значение, которое мы можем использовать для расчета для нахождения самого последнего воскресенья и предыдущего Понедельник немного легче.

по модулю 7 на понедельник

DATEDIFF(day, 0, GETDATE()) % 7 использует тот факт, что DATEPART(day, 0) (что с риском переоценки точки совпадает с DATEPART(day, '1/1/1900')) возвращает 2 (понедельник). 1 Следовательно, DATEDIFF(day, 0, GETDATE()) % 7 всегда будет указывать количество дней с понедельника до текущей даты.

1 Если вы не изменили поведение по умолчанию с помощью DATEFIRST.

Самый последний понедельник

Это почти слишком тривиально для его собственного заголовка, но на этом этапе мы можем собрать все, что у нас так далеко:

  • GETDATE() - DATEDIFF(day, 0, GETDATE()) %7 дает вам самый последний понедельник
  • DATEDIFF(day, 0, GETDATE() - DATEDIFF(day, 0, GETDATE()) %7) дает вам самый последний понедельник как целую дату, без временной части.

Но плакат хотел все с понедельника по воскресенье ...

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

  • t.yourdate >= a.lastmonday - 7 возвращает записи с датами, начиная с (или включая) полуночи второго по времени последнего понедельника
  • t.yourdate < a.lastmonday возвращает записи с датами до (но не включая) полуночи самого последнего понедельника.

Я большой сторонник написания кода, который легко понять, как для вас, так и для разработчика, который придет после вас через год (который также может быть вами). Я считаю, что ответ, который я опубликовал ранее, должен быть понятен даже начинающим программистам на T-SQL. Однако ответ t-clausen.dk является кратким, хорошо работает и может встречаться в рабочем коде, поэтому я хотел дать некоторое объяснение, чтобы помочь будущим посетителям понять, почему это работает.

4 голосов
/ 23 августа 2011

Я понял, что мой код слишком сложен, поэтому я изменил свой ответ на это.Минус один после getdate () исправляет это, возвращая последний понедельник воскресенья вместо понедельника этой недели (завтра, если сегодня воскресенье).

SELECT t.* 
FROM <table> t CROSS JOIN
(SELECT DATEDIFF(week, 0, getdate() - 1) * 7 lastmonday) a
WHERE t.yourdate >= a.lastmonday - 7 and yourdate < a.lastmonday

Старый код

SELECT t.* 
FROM <table> t CROSS JOIN
(SELECT DATEDIFF(day, 0, getdate() - DATEDIFF(day, 0, getdate()) %7) lastmonday) a
WHERE t.yourdate >= a.lastmonday - 7 and yourdate < a.lastmonday
3 голосов
/ 23 августа 2011

Вы не указали какой диалект SQL, поэтому я отвечу за T-SQL, что мне лучше всего известно, а вы использовали тег tsql.

В T-SQL используйтефункция DATEPART для поиска дня недели.Когда вы знаете текущий день недели, вы можете получить дату самого последнего воскресенья и понедельник до него.

В хранимой процедуре это проще - по крайней мере, более читабельно и проще в обслуживании,по моему мнению - рассчитать значения для самого последнего воскресенья и предыдущего понедельника и сохранить значения в переменных.Затем эти переменные могут быть использованы в вычислениях позже в этой процедуре.

CREATE PROCEDURE SomeProcedure
AS
  DECLARE @CurrentDayOfWeek int, @LastSunday date, @LastMonday date
  SET @CurrentWeekday = DATEPART(weekday, GETDATE())
  -- Count backwards from today to get to the most recent Sunday.
  -- (@CurrentWeekday % 7) - 1 will give the number of days since Sunday; 
  -- -1 negates for subtraction.
  SET @LastSunday = DATEADD(day,  -1 * (( @CurrentWeekday % 7) - 1), GETDATE())
  -- Preceding Monday is obviously six days before last Sunday.
  SET @LastMonday = DATEADD(day, -6, @LastSunday)

  SELECT ReportColumn1, ReportColumn2
  FROM ReportTable
  WHERE DateColumn BETWEEN @LastMonday AND @LastSunday

Если вам необходимо выполнить вычисления в операторе SELECT или в представлении, теперь тривиально, что мы разработалишаги, хотя сам запрос немного сложнее:

  SELECT ReportColumn1, ReportColumn2
  FROM ReportTable
  WHERE DateColumn 
  BETWEEN 
  (
      -- Last Monday is six days before...
      DATEADD(day, -6, 
         -- ... last Sunday.
         DATEADD(day,  -1 * (( DATEPART(weekday, GETDATE()) % 7) - 1), GETDATE())
      )
  )
  AND
  (
      -- Last Sunday has to be calculated again each time it is used inline.
      DATEADD(day,  -1 * (( DATEPART(weekday, GETDATE()) % 7) - 1), GETDATE())
  )

Добавленные мной круглые скобки не обязательны, но служат только для того, чтобы помочь вам увидеть, как строится предложение WHERE.обратите внимание, что они используют тип данных SQL 2008 date;для типа данных datetime может потребоваться выполнить собственное преобразование / усечение для сравнения целых дат вместо значений даты плюс время.

1 голос
/ 26 ноября 2014

Короткий код в ответе @ t-clausen.dk не работает для воскресенья и не первый результат, который я нашел в Google.Чтобы проверить их, попробуйте.

DECLARE @date as DATETIME;
SET @date = '2014-11-23 10:00:00'; -- Sunday at 10a

SELECT DATEADD(wk, DATEDIFF(wk,0,@date), 0) -- 2014-11-24 00:00:00
SELECT CAST(DATEDIFF(week, 0, @date)*7 as datetime) -- 2014-11-24 00:00:00
SELECT CAST(DATEDIFF(day, 0, CAST(@date as DATETIME) - DATEDIFF(day, 0,@date) %7) as DATETIME) --2014-11-17 00:00:00
SELECT DATEADD(wk, DATEDIFF(wk,0,@date-1), 0) --2014-11-17 00:00:00

Таким образом, вы должны использовать длинный код из исходного ответа или измененный короткий код.

SELECT t.* 
FROM <table> t CROSS JOIN
(SELECT DATEDIFF(week, 0, getdate() - 1) * 7 lastmonday) a
WHERE t.yourdate >= a.lastmonday - 7 and yourdate < a.lastmonday
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...