帮助将本周查询从Oracle PL / SQL转换为SQL Server 2008

时间:2010-03-08 19:27:32

标签: sql oracle tsql sql-server-2008 plsql

我有以下在我的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 个答案:

答案 0 :(得分:3)

我修改了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

我认为这比我的previous answer更好,因为它不依赖于具有特定行数的另一个表。生成的周数可以通过仅改变一个数字来改变:SELECT DATEADD(WEEK, -3, GETDATE()) AS starting_date中的3。包括当前周,该数字表示应显示本周之前的额外周数。

在过去几周内迭代到现在,不包括当前周

更新:,这是一个排除本周的版本:

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

答案 1 :(得分:2)

使用(但不要忘记vote for Sarah):

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标准btw)。使用:

    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
    
  3. this link for explaining how to get the first day of the week。要更改周数,请更改DATEADD(dd, 4*7, GETDATE()),其中4表示您想要生成的周数。

答案 2 :(得分:1)

你需要的只是一套:

;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;

但是我会提醒您,如果您的数据是日期时间,并且您要将这些边界用于查询范围,则应使用开放式范围,例如: &gt; = 03/07和&lt; 03/14。这样你就不会错过23:59:59到午夜之间发生的任何行;虽然它们可能很罕见,但它们可能很重要。

答案 3 :(得分:0)

这就是我想出的。它略显笨拙,因为它依赖于我的表中至少有一个行&gt; =我想要选择的周数。我可以稍微调整它以包括当前周。

   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
...

答案 4 :(得分:0)

根据您的代码:

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