1

当我对 SQL (MSSQL) 还很陌生时,我正在重新审视我为报告编写的一些旧代码。它做了它应该做的事情,但它不是最漂亮或最有效的。

下面的虚拟代码模仿了我目前所拥有的。在这里,我试图计算过去 5 周内未平仓合约的数量。对于此示例,如果合同的开始日期发生在给定周之前或之后,并且结束日期发生在给定周期间或之后,则合同被认为是未结的。

dbo.GetWeekStart(@Date DATETIME, @NumOfWeeks INT, @FirstDayOfWeek CHAR(3)) 是一个函数,它将根据为指定周数提供的日期返回每周的第一天。即 SELECT * FROM dbo.GetWeekStart('20120719', -2, 'MON') 将返回 2012 年 7 月 19 日之前的 2 个星期一。

我怎样才能简化这个?我认为有人可以在没有循环的情况下执行此操作,但我无法弄清楚。

DECLARE @RunDate DATETIME,
    @Index INT,
    @RowCount INT,
    @WeekStart DATETIME,
    @WeekEnd DATETIME

DECLARE @Weeks TABLE
(
    WeekNum INT IDENTITY(0,1),
    WeekStart DATETIME,
    WeekEnd DATETIME
)

DECLARE @Output TABLE
(
    WeekStart DATETIME,
    OpenContractCount INT
)

SET @RunDate = GETDATE()

INSERT INTO @Weeks (WeekStart, WeekEnd)
SELECT WeekStart,
DATEADD(ss,-1,DATEADD(ww,1,WeekStart))
FROM dbo.[GetWeekStart](@RunDate, -5, 'MON') 

SET @RowCount = (SELECT COUNT(*) FROM @Weeks)
SET @Index = 0

WHILE @Index < @RowCount
BEGIN
    SET @WeekStart = (SELECT WeekStart FROM @Weeks WHERE WeekNum = @Idx)
    SET @WeekEnd = (SELECT WeekEnd FROM @Weeks WHERE WeekNum = @Idx)

    INSERT INTO @Output (WeekStart, OpenContractCount)
    SELECT @WeekStart,
    COUNT(*)
    FROM Contracts c
    WHERE c.StartDate <= @WeekEnd
    AND ISNULL(c.EndDate, GETDATE()) >= @WeekStart

    SET @Index = @Index + 1
END
SELECT * FROM @Output
4

2 回答 2

0

这样做很快,但它应该工作

/*CTE generates Start & End Dates for 5 weeks
Start Date = Sunday of week @ midnight
End Date = Sunday of next week @ midnight
*/
WITH    weeks
      AS ( SELECT   DATEADD(ww, -4,
                            CAST(FLOOR(CAST(GETDATE() - ( DATEPART(dw,
                                                          GETDATE()) - 1 ) AS FLOAT)) AS DATETIME)) AS StartDate
           UNION ALL
           SELECT   DATEADD(wk, 1, StartDate)
           FROM     weeks
           WHERE    DATEADD(wk, 1, StartDate) <= GETDATE()
         )
SELECT  w.StartDate ,
        COUNT(*) AS OpenContracts
FROM    dbo.Contracts c
        LEFT   JOIN weeks w ON c.StartDate < DATEADD(d, 7, w.StartDate)
                               AND ISNULL(c.EndDate, GETDATE()) >= w.StartDate
GROUP BY w.StartDate
于 2012-07-19T15:59:30.433 回答
0

我看不出为什么这不起作用:

DECLARE @RunDate DATETIME = GETDATE()

SELECT  WeekStart, COUNT(*)
FROM    Contracts c
        INNER JOIN dbo.[GetWeekStart](@RunDate, -5, 'MON') 
            ON c.StartDate < DATEADD(WEEK, 1, WeekStart)
            AND (c.EndDate IS NULL OR c.EndDate >= @WeekStart)
GROUP BY WeekStart

我不确定您是如何在函数中生成日期的,以防万一您使用循环/递归 CTE,我将包含一个不使用循环/游标等的查询。

DECLARE @RunDate DATETIME = GETDATE()

-- SET DATEFIRST AS 1 TO ENSURE MONDAY IS THE FIRST DAY OF THE WEEK
-- CHANGE THIS TO SIMULATE CHANGING YOUR WEEKDAY INPUT TO db
SET DATEFIRST 1 

-- SET RUN DATE TO BE THE START OF THE WEEK
SET @RunDate = CAST(DATEADD(DAY, 1 - DATEPART(WEEKDAY, @RunDate), @RunDate) AS DATE)

;WITH Weeks AS
(   SELECT  TOP 5 -- CHANGE THIS TO CHANGE THE WEEKS TO RUN
            DATEADD(WEEK, 1 - ROW_NUMBER() OVER(ORDER BY Object_ID), @RunDate) [WeekStart]
    FROM    sys.All_Objects
)
SELECT  WeekStart, COUNT(*)
FROM    Contracts c
        INNER JOIN Weeks
            ON c.StartDate < DATEADD(WEEK, 1, WeekStart)
            AND (c.EndDate IS NULL OR c.EndDate >= @WeekStart)
GROUP BY WeekStart
于 2012-07-19T16:58:23.540 回答