помогите перевести запрос на этой неделе из Oracle PL / SQL в SQL Server 2008 - PullRequest
3 голосов
/ 08 марта 2010

У меня есть следующий запрос, который выполняется в моей базе данных Oracle, и я хочу иметь эквивалент для базы данных SQL Server 2008:

SELECT TRUNC( /* Midnight Sunday */
         NEXT_DAY(SYSDATE, 'SUN') - (7*LEVEL)
       ) AS week_start,
       TRUNC( /* 23:59:59 Saturday */
         NEXT_DAY(NEXT_DAY(SYSDATE, 'SUN') - (7*LEVEL), 'SAT') + 1
       ) - (1/(60*24)) + (59/(60*60*24)) AS week_end
FROM DUAL
CONNECT BY LEVEL <= 4 /* Get the past 4 weeks */

Что делает запрос, так это получает начало недели и конец недели за последние 4 недели. Количество недель является произвольным и должно быть легко изменено в запросе SQL Server, который я хочу. Он генерирует данные, подобные следующим:

WEEK_START          WEEK_END
2010-03-07 00:00:00 2010-03-13 23:59:59
2010-02-28 00:00:00 2010-03-06 23:59:59
...

Часть, в которой я сейчас застрял при переводе, - CONNECT BY LEVEL, поскольку, похоже, SQL Server 2008 не имеет эквивалента. Я бы предпочел просто настроить строку, например CONNECT BY LEVEL <= 4, и сделать так, чтобы запрос генерировал больше или меньше недель (т. Е. Мне не нужно настраивать несколько операторов UNION ALL).

Редактировать: вот что у меня так далеко, что получает начало и конец текущей недели:

   SELECT week_start,
          DATEADD(SECOND, -1, DATEADD(DAY, 7, week_start)) AS week_end
   FROM (
          SELECT CAST(
                   CONVERT(
                     VARCHAR(10),
                     DATEADD(DAY, 1-DATEPART(DW, GETDATE()), GETDATE()),
                     111
                   ) AS DATETIME
                 ) AS week_start
        ) AS week_start_view

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

Ответы [ 5 ]

3 голосов
/ 10 марта 2010

Я изменил OMG Ponies 'ответ, потому что он выглядел как хорошая идея, он просто имел неправильное значение week_end для каждой недели и также показывал будущие недели вместо прошлых недель. Я придумал следующее:

WITH dates AS (
  SELECT DATEADD(
           DD, 
           1 - DATEPART(DW, CONVERT(VARCHAR(10), starting_date, 111)), 
           CONVERT(VARCHAR(10), starting_date, 111)
         ) AS midnight
  FROM (
         SELECT DATEADD(WEEK, -3, GETDATE()) AS starting_date
       ) AS starting_date_view

  UNION ALL

  SELECT DATEADD(DD, 7, midnight)
  FROM dates
  WHERE DATEADD(DD, 7, midnight) < GETDATE()
) SELECT midnight AS week_start,
         DATEADD(SS, -1, DATEADD(DAY, 7, midnight)) AS week_end
  FROM dates

Генерирует последние 4 недели:

week_start                 week_end
2010-02-14 00:00:00.000    2010-02-20 23:59:59.000
2010-02-21 00:00:00.000    2010-02-27 23:59:59.000
2010-02-28 00:00:00.000    2010-03-06 23:59:59.000
2010-03-07 00:00:00.000    2010-03-13 23:59:59.000

Это лучше, чем мой предыдущий ответ , я думаю, потому что он не основан на другой таблице, имеющей определенное количество строк. Количество сгенерированных недель можно изменить, изменив только одну цифру: 3 в SELECT DATEADD(WEEK, -3, GETDATE()) AS starting_date. Текущая неделя включена, и эта цифра показывает, сколько дополнительных недель до текущей недели должно быть показано.

Перебирать прошлые недели до настоящего времени, исключая текущую неделю

Обновление: , и вот версия, исключающая текущую неделю:

WITH dates AS (
  SELECT DATEADD(
           DD,
           1 - DATEPART(DW, CONVERT(VARCHAR(10), starting_date, 111)), 
           CONVERT(VARCHAR(10), starting_date, 111)
         ) AS midnight_sunday
  FROM (
         SELECT DATEADD(WEEK, -4, GETDATE()) AS starting_date
       ) AS starting_date_view

  UNION ALL

  SELECT DATEADD(DD, 7, midnight_sunday)
  FROM dates
  WHERE DATEADD(DD, 7, midnight_sunday) <
        DATEADD(
          DD,
          1 - DATEPART(DW, CONVERT(VARCHAR(10), GETDATE(), 111)), 
          CONVERT(VARCHAR(10), GETDATE(), 111)
        )
) SELECT midnight_sunday AS week_start,
         DATEADD(SS, -1, DATEADD(DAY, 7, midnight_sunday)) AS week_end
  FROM dates

Результаты:

week_start                 week_end
2010-02-07 00:00:00.000    2010-02-13 23:59:59.000
2010-02-14 00:00:00.000    2010-02-20 23:59:59.000
2010-02-21 00:00:00.000    2010-02-27 23:59:59.000
2010-02-28 00:00:00.000    2010-03-06 23:59:59.000

Итерация за прошедшие месяцы до настоящего момента

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

WITH dates AS (
  SELECT CAST(
        FLOOR(CAST(starting_date AS DECIMAL(12, 5))) -
        (DAY(starting_date) - 1) AS DATETIME
      ) AS month_start
  FROM (
         SELECT DATEADD(MONTH, -3, GETDATE()) AS starting_date
       ) AS starting_date_view

  UNION ALL

  SELECT DATEADD(MONTH, 1, month_start)
  FROM dates
  WHERE DATEADD(MONTH, 1, month_start) < GETDATE()
) SELECT month_start,
         DATEADD(SS, -1, DATEADD(MONTH, 1, month_start)) AS month_end
  FROM dates
  ORDER BY month_start DESC
2 голосов
/ 09 марта 2010

Используйте (но не забудьте голосовать за Сару ):

WITH dates AS (
    SELECT DATEADD(DD, 
                   1 - DATEPART(DW, CONVERT(VARCHAR(10), starting_date, 111)), 
                   CONVERT(VARCHAR(10), starting_date, 111)
                   ) AS midnight
      FROM (SELECT DATEADD(WEEK, -3, GETDATE()) AS starting_date) AS starting_date_view
    UNION ALL
    SELECT DATEADD(DD, 7, midnight)
      FROM dates
     WHERE DATEADD(DD, 7, midnight) < GETDATE()) 
SELECT midnight AS week_start,
       DATEADD(SS, -1, DATEADD(DAY, 7, midnight)) AS week_end
  FROM dates

Ранее:

  1. Установите для первого дня недели воскресенье с помощью команды SET DATEFIRST :

    SET DATEFIRST 7
    
  2. SQL Server 2005+, эквивалентный Oracle CONNECT BY LEVEL, является рекурсивным CTE (кстати, стандартом ANSI). Использование:

    WITH dates AS (
       SELECT DATEADD(DD, 
                      1 - DATEPART(DW, CONVERT(VARCHAR(10), GETDATE(), 111)), 
                      CONVERT(VARCHAR(10), GETDATE(), 111)) AS date
       UNION ALL
       SELECT DATEADD(dd, 7, d.date)
         FROM dates d
        WHERE DATEADD(dd, 7, d.date) <= DATEADD(dd, 4*7, GETDATE()))
    SELECT t.date AS week_start,
           DATEADD(ss, -1, DATEADD(DAY, 7, t.date)) AS week_end
      FROM dates t
    

См. эту ссылку для объяснения того, как получить первый день недели . Чтобы изменить количество недель, измените DATEADD(dd, 4*7, GETDATE()), где 4 представляет количество недель, которое вы хотите сгенерировать.

1 голос
/ 08 марта 2010

Все, что вам нужно, это набор:

;WITH cte(n) AS
(
    SELECT 0
    UNION ALL SELECT 1
    UNION ALL SELECT 2
    UNION ALL SELECT 3
)
  SELECT week_start,
          DATEADD(SECOND, -1, DATEADD(DAY, 7, week_start)) AS week_end
   FROM (
          SELECT CAST(
                   CONVERT(
                     VARCHAR(10),
                     DATEADD(WEEK, -n, DATEADD(DAY, 1-DATEPART(DW, GETDATE()), GETDATE())),
                     111
                   ) AS DATETIME
                 ) AS week_start
                           FROM cte
        ) AS week_start_view;

Однако я предупреждаю вас, что если ваши данные относятся к дате и времени, и вы собираетесь использовать эти границы для диапазонов запросов, вам следует использовать открытый диапазон, например > = 03/07 и <03/14. Таким образом, вы не пропустите ни одной строки, которая произошла с 23:59:59 до полуночи; какими бы редкими они ни были, они могут быть важны. </p>

0 голосов
/ 08 марта 2010

на основе вашего кода:

SELECT DATEADD(day, weeks.week * -7, week_start) AS week_start,
       DATEADD(SECOND, -1, DATEADD(DAY, (weeks.week-1) * -7, week_start)) AS week_end
FROM (
      SELECT CAST(
               CONVERT(
                 VARCHAR(10),
                 DATEADD(DAY, 1-DATEPART(DW, GETDATE()), GETDATE()),
                 111
               ) AS DATETIME
             ) AS week_start
     ) AS week_start_view,
     ( SELECT 0 AS week UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) AS weeks
0 голосов
/ 08 марта 2010

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

   SELECT week_start,
          DATEADD(SECOND, -1, DATEADD(DAY, 7, week_start)) AS week_end
   FROM (
          SELECT CAST(
                   CONVERT(
                     VARCHAR(10),
                     DATEADD(
                       DAY,
                       1-DATEPART(DW, GETDATE()),
                       DATEADD(DAY, -7*level, GETDATE())
                     ),
                     111
                   ) AS DATETIME
                 ) AS week_start
          FROM (
                 SELECT level
                 FROM (
                        SELECT ROW_NUMBER() OVER(ORDER BY RAND()) AS level
                        FROM my_table_with_at_least_21_rows
                      ) AS all_rows_view
                 WHERE level <= 21
               ) AS level_view
        ) AS week_start_view

Это получает последние 21 недели, начиная с последней недели. Вот пример данных:

week_start                  week_end
2010-02-28 00:00:00.000     2010-03-06 23:59:59.000
2010-02-21 00:00:00.000     2010-02-27 23:59:59.000
2010-02-14 00:00:00.000     2010-02-20 23:59:59.000
2010-02-07 00:00:00.000     2010-02-13 23:59:59.000
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...