查询当月每周数据的优化

时间:2017-01-25 12:05:37

标签: sql sql-server sql-server-2008 sql-server-2012

需要SQL服务器查询优化方面的帮助,如下所示:

var date = "2001-05"

func stringToDate(_ date: String) -> Date? {
    let df = DateFormatter()
    df.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale!
    df.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZ"

    return df.date(from: date)
}

print(stringToDate(date))

结果应如下:

    enter declare @sDate datetime  
declare @eDate datetime 

SET @sDate = '2017-01-01'  
SET @eDate = '2017-01-31'

SELECT    
    @sDate AS [StartDate], 
    DATEADD(day,6, @sDate) [ENDDATE],                       
    SUM(CASE WHEN GS.[Status] = 'Open' THEN 1 ELSE 0 END) [rcOpen],  
    SUM(CASE WHEN GS.[Status] = 'Closed'  THEN 1 ELSE 0 END) [rcClosed]
    FROM GS
    WHERE     
    (GS.[ModifiedDate] > @sDate)
    AND 
    (GS.[ModifiedDate] <= DATEADD(day,6, @sDate))
UNION
SELECT    
    DATEADD(day,7, @sDate) AS [StartDate], 
    DATEADD(day,13, @sDate) [ENDDATE],                      
    SUM(CASE WHEN GS.[Status] = 'Open' THEN 1 ELSE 0 END) [rcOpen],  
    SUM(CASE WHEN GS.[Status] = 'Closed'  THEN 1 ELSE 0 END) [rcClosed]
    FROM GS
    WHERE     
    (GS.[ModifiedDate] > DATEADD(day,7, @sDate))
    AND 
    (GS.[ModifiedDate] <= DATEADD(day,13, @sDate))
UNION
SELECT    
    DATEADD(day,14, @sDate) AS [StartDate], 
    DATEADD(day,20, @sDate) [ENDDATE],                      
    SUM(CASE WHEN GS.[Status] = 'Open' THEN 1 ELSE 0 END) [rcOpen],  
    SUM(CASE WHEN GS.[Status] = 'Closed'  THEN 1 ELSE 0 END) [rcClosed]
    FROM GS
    WHERE     
    (GS.[ModifiedDate] > DATEADD(day,7, @sDate))
    AND 
    (GS.[ModifiedDate] <= DATEADD(day,20, @sDate))
UNION
SELECT    
    DATEADD(day,21, @sDate) AS [StartDate], 
    DATEADD(day,27, @sDate) [ENDDATE],                      
    SUM(CASE WHEN GS.[Status] = 'Open' THEN 1 ELSE 0 END) [rcOpen],  
    SUM(CASE WHEN GS.[Status] = 'Closed'  THEN 1 ELSE 0 END) [rcClosed]
    FROM GS
    WHERE     
    (GS.[ModifiedDate] > DATEADD(day,21, @sDate))
    AND 
    (GS.[ModifiedDate] <= DATEADD(day,27, @sDate))
UNION

SELECT    
    DATEADD(day,27, @sDate) AS [StartDate], 
    @eDate [ENDDATE],                       
    SUM(CASE WHEN GS.[Status] = 'Open' THEN 1 ELSE 0 END) [rcOpen],  
    SUM(CASE WHEN GS.[Status] = 'Closed'  THEN 1 ELSE 0 END) [rcClosed]
    FROM GS
    WHERE     
    (GS.[ModifiedDate] > DATEADD(day,27, @sDate))
    AND 
    (GS.[ModifiedDate] <= @eDate)

可能需要对每周数据使用CTE(公用表表达式),如此处所述

how to get the start and end dates of all weeks between two dates in SQL server?

StartDate               ENDDATE                 rcOpen  rcClosed
2017-01-01 00:00:00.000 2017-01-07 00:00:00.000 NULL    NULL
2017-01-08 00:00:00.000 2017-01-14 00:00:00.000 NULL    NULL
2017-01-15 00:00:00.000 2017-01-21 00:00:00.000 12      5
2017-01-22 00:00:00.000 2017-01-28 00:00:00.000 NULL    NULL
2017-01-28 00:00:00.000 2017-01-31 00:00:00.000 NULL    NULL  

5 个答案:

答案 0 :(得分:0)

如果您无法在数据库中添加Dates或Numbers表,那么使用您提到的CTE生成的派生表可能是最好的方法:

declare @sDate datetime,
        @eDate datetime;

select  @sDate = '2013-02-25',
        @eDate = '2013-03-25';


;with cte as
(
  select @sDate StartDate, 
    DATEADD(wk, DATEDIFF(wk, 0, @sDate), 6) EndDate
  union all
  select dateadd(ww, 1, StartDate),
    dateadd(ww, 1, EndDate)
  from cte
  where dateadd(ww, 1, StartDate)<=  @eDate
)
SELECT
    CTE.StartDate
    ,CTE.EndDate
    ,SUM(CASE WHEN GS.[Status] = 'Open' THEN 1 ELSE 0 END) [rcOpen]
    ,SUM(CASE WHEN GS.[Status] = 'Closed'  THEN 1 ELSE 0 END) [rcClosed]
FROM CTE
    LEFT JOIN GS
        ON CTE.StartDate < GS.[ModifiedDate]
            AND CTE.EndDate >= GS.[ModifiedDate]
GROUP BY CTE.StartDate
        ,CTE.EndDate
ORDER BY CTE.StartDate

答案 1 :(得分:0)

此查询的性能优于递归CTE。

declare @sDate datetime = '2017-01-01'; 
declare @eDate datetime = '2017-01-31';

WITH X AS (
SELECT DISTINCT 
      DATEADD(DAY, -  (DATEPART(WEEKDAY, [Dates])-1), [Dates]) [WeekStart]
    , DATEADD(DAY, 7- (DATEPART(WEEKDAY, [Dates])), [Dates])   [WeekEnd]
FROM (
        SELECT DISTINCT DATEADD(DAY , rn -1 , @sDate) [Dates]
        FROM (
                Select TOP (DATEDIFF(DAY, @sDate, @eDate)) 
                      ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) rn
                FROM master..spt_values a 
                      CROSS JOIN master..spt_values b
             )a
     ) b
)
SELECT  [WeekStart]
      , [WeekEnd]
      , SUM(CASE WHEN GS.[Status] = 'Open' THEN 1 ELSE 0 END) [rcOpen],  
      , SUM(CASE WHEN GS.[Status] = 'Closed'  THEN 1 ELSE 0 END) [rcClosed]
FROM X 
LEFT JOIN GS  ON GS.[ModifiedDate] <= CTE.[WeekEnd] 
                AND GS.[ModifiedDate] >= CTE.[WeekStart]

答案 2 :(得分:0)

另一种方法

declare @sDate datetime  
declare @eDate datetime 

SET @sDate = '2017-01-01'  
SET @eDate = '2017-01-31'

--A recursive CTE for fetching the weeks range
;WITH CTE AS
(
    SELECT @sDate SDATE
    , DATEADD(DD,6,@sDate) AS TO_DTE

    UNION ALL 

    SELECT DATEADD(DD,1,TO_DTE)
    , CASE 
        WHEN DATEADD(DD, 7, TO_DTE) > @eDate
            THEN @eDate
        ELSE DATEADD(DD, 7, TO_DTE)
        END
    FROM CTE
    WHERE DATEADD(DD, 1, TO_DTE) <= @eDate


)
/* An Intermediate result of CTE to better understand
+-------------------------+-------------------------+
|          SDATE          |         TO_DTE          |
+-------------------------+-------------------------+
| 2017-01-01 00:00:00.000 | 2017-01-07 00:00:00.000 |
| 2017-01-08 00:00:00.000 | 2017-01-14 00:00:00.000 |
| 2017-01-15 00:00:00.000 | 2017-01-21 00:00:00.000 |
| 2017-01-22 00:00:00.000 | 2017-01-28 00:00:00.000 |
| 2017-01-29 00:00:00.000 | 2017-01-31 00:00:00.000 |
+-------------------------+-------------------------+
*/

SELECT CTE.SDATE
    ,CTE.TO_DTE
    ,SUM(CASE WHEN GS.[Status] = 'Open' THEN 1 ELSE 0 END) [rcOpen]
    ,SUM(CASE WHEN GS.[Status] = 'Closed'  THEN 1 ELSE 0 END) [rcClosed]
FROM GS
    JOIN CTE
        ON GS.[ModifiedDate] > CTE.SDATE
            AND GS.[ModifiedDate] <= CTE.TO_DTE
GROUP BY CTE.SDATE
        ,CTE.TO_DTE
ORDER BY CTE.SDATE

答案 3 :(得分:0)

$scope

在@iamdave中进行少量更改后,上面的回答将是正确答案

答案 4 :(得分:-1)

也许您可以使用UNION ALL而不只是UNION