SQL求两次累计和的平衡点

SQL getting balance point of two cumulative sums

下面我提供了一个代码,它在一定程度上完成了这个有趣的任务,但由于我的 SQL 知识限制可能设计不佳。主要问题是,查询在执行时给出 random (更确切地说是 3 个不同的)结果。我的猜测是,嵌套查询中的行按 "randomly" 选定列排序,这就是最终结果不同的原因(平衡点取决于顺序)。

内部 SELECTGROUP 创建一个 r-dates 列表和两个累积和,例如:

rIndex  r             TotalPerDay   CumulativeSum1  CumulativeSum2
1       02.05.2019    92,81         92,81           0
2       03.05.2019    24,81         117,61          0
3       06.05.2019    43,79         161,40          60
4       07.05.2019    78,65         240,05          120
5       09.05.2019    33,99         274,04          180
6       10.05.2019    73,22         347,26          240
7       13.05.2019    19,24         366,50          300
8       14.05.2019    150,77        517,27          360
9       15.05.2019    22,69         539,95          420
10      16.05.2019    4,96          544,91          480
11      17.05.2019    17,45         562,36          540
12      20.05.2019    27,19         589,55          600
13      21.05.2019    12,45         602,00          660
14      22.05.2019    18,08         620,08          720
15      23.05.2019    3,49          623,57          780
16      24.05.2019    10,51         634,09          840
17      27.05.2019    6,19          640,28          900
18      28.05.2019    3,01          643,29          960
19      29.05.2019    2,68          645,97          1020
20      30.05.2019    184,51        830,48          1080

样本数据的尝试在附件中(由于下面的评论而被删除)。

在第二个嵌套 SELECT 中,我找到了一个平衡点,这是一个(第一个)日期,其中 CumulativeSum1 > CumulativeSum2。然后我必须找到一个带总和的天数索引(因为也有没有数据的天数),这就是最终结果;它是下面查询中最顶层的 SELECT

DECLARE @eDate as Date
DECLARE @DayLimit INT
SET @DayLimit = 60  -- let's assume a constant here
SET @eDate = DATEFROMPARTS('2019','05','31')

-- get balance point INDEX over non-empty days
SELECT (SELECT COUNT(cDate) FROM Calendar WHERE KindOfDay = 'BANKDAY' AND cDate BETWEEN GETDATE() AND SRC3.BalanceDate) as rIndex
FROM
    (    
    SELECT TOP 1 SRC2.rDate   -- get first balance point (date)
    FROM
        (
        SELECT 
             ROW_NUMBER() OVER (ORDER BY SRC.rDate) as RowNo
            ,SRC.rDate 
            ,SRC.TotalPerDay      -- not required for processing, included just for info and check
            ,(SELECT (SUM((eTime-ISNULL(rDura,0))/60)) FROM MyTable1 as MT WHERE MT.r <= SRC.rDate AND MT.r < @eDate)         as CumulativeSum1
            ,((SELECT COUNT(cDate) FROM Calendar WHERE KindOfDay = 'BANKDAY' AND cDate BETWEEN GETDATE() AND SRC.rDate) * @DayLimit) as CumulativeSum2
        FROM (
            SELECT   
                  CASE  
                      WHEN CAST(r as DATE) < CAST(GETDATE() as date)  
                      THEN DATEADD(dd,-1,CAST(GETDATE() as date))                
                      ELSE CAST(r as date)                           
                  END as rDate, 
                  SUM((eTime-ISNULL(rDura,0))/60) as TotalPerDay      
            FROM MyTable1 
            WHERE r < @eDate
            GROUP BY  -- group by non-empty dates, group all past dates to yesterday
                   CASE  
                       WHEN CAST(r as DATE) < CAST(GETDATE() as date)  
                      THEN DATEADD(dd,-1,CAST(GETDATE() as date))               
                      ELSE CAST(r as date)                             
                   END                                
        ) as SRC                          
        --ORDER BY rDate
        ) as SRC2  -- compiled list of sums per day
    WHERE SRC2.CumulativeSum2 > SRC2.CumulativeSum1;    -- balance condition
) as SRC3

对于明显的问题虚心求教:

此外,我刚刚意识到最上面的查询存在差异,我在其中获得银行工作日的索引,但索引应该超过非空银行工作日...

一些示例数据:

-------  CALENDAR TABLE  --------------------------------------------------------------------

CREATE TABLE [dbo].[Calendar](
    [cDate] [datetime] NOT NULL,
    [KindOfDay] [varchar](10) NOT NULL
PRIMARY KEY CLUSTERED 
(
    [cDate] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]


INSERT INTO [dbo].[Calendar] ([cDate],[KindOfDay])  VALUES 
    ('2019-04-20 00:00:00.000', 'SATURDAY'),
    ('2019-04-21 00:00:00.000', 'SUNDAY'),
    ('2019-04-22 00:00:00.000', 'HOLIDAY'),
    ('2019-04-23 00:00:00.000', 'BANKDAY'),
    ('2019-04-24 00:00:00.000', 'BANKDAY'),
    ('2019-04-25 00:00:00.000', 'BANKDAY'),
    ('2019-04-26 00:00:00.000', 'BANKDAY'),
    ('2019-04-27 00:00:00.000', 'SATURDAY'),
    ('2019-04-28 00:00:00.000', 'SUNDAY'),
    ('2019-04-29 00:00:00.000', 'BANKDAY'),
    ('2019-04-30 00:00:00.000', 'BANKDAY'),
    ('2019-05-01 00:00:00.000', 'HOLIDAY'),
    ('2019-05-02 00:00:00.000', 'BANKDAY'),
    ('2019-05-03 00:00:00.000', 'BANKDAY'),
    ('2019-05-04 00:00:00.000', 'SATURDAY'),
    ('2019-05-05 00:00:00.000', 'SUNDAY'),
    ('2019-05-06 00:00:00.000', 'BANKDAY'),
    ('2019-05-07 00:00:00.000', 'BANKDAY'),
    ('2019-05-08 00:00:00.000', 'HOLIDAY'),
    ('2019-05-09 00:00:00.000', 'BANKDAY'),
    ('2019-05-10 00:00:00.000', 'BANKDAY'),
    ('2019-05-11 00:00:00.000', 'SATURDAY'),
    ('2019-05-12 00:00:00.000', 'SUNDAY'),
    ('2019-05-13 00:00:00.000', 'BANKDAY'),
    ('2019-05-14 00:00:00.000', 'BANKDAY'),
    ('2019-05-15 00:00:00.000', 'BANKDAY'),
    ('2019-05-16 00:00:00.000', 'BANKDAY'),
    ('2019-05-17 00:00:00.000', 'BANKDAY'),
    ('2019-05-18 00:00:00.000', 'SATURDAY'),
    ('2019-05-19 00:00:00.000', 'SUNDAY'),
    ('2019-05-20 00:00:00.000', 'BANKDAY'),
    ('2019-05-21 00:00:00.000', 'BANKDAY'),
    ('2019-05-22 00:00:00.000', 'BANKDAY'),
    ('2019-05-23 00:00:00.000', 'BANKDAY'),
    ('2019-05-24 00:00:00.000', 'BANKDAY'),
    ('2019-05-25 00:00:00.000', 'SATURDAY'),
    ('2019-05-26 00:00:00.000', 'SUNDAY'),
    ('2019-05-27 00:00:00.000', 'BANKDAY'),
    ('2019-05-28 00:00:00.000', 'BANKDAY'),
    ('2019-05-29 00:00:00.000', 'BANKDAY'),
    ('2019-05-30 00:00:00.000', 'BANKDAY'),
    ('2019-05-31 00:00:00.000', 'BANKDAY'),
    ('2019-06-01 00:00:00.000', 'SATURDAY'),
    ('2019-06-02 00:00:00.000', 'SUNDAY'),
    ('2019-06-03 00:00:00.000', 'BANKDAY'),
    ('2019-06-04 00:00:00.000', 'BANKDAY'),
    ('2019-06-05 00:00:00.000', 'BANKDAY'),
    ('2019-06-06 00:00:00.000', 'BANKDAY'),
    ('2019-06-07 00:00:00.000', 'BANKDAY'),
    ('2019-06-08 00:00:00.000', 'SATURDAY'),
    ('2019-06-09 00:00:00.000', 'SUNDAY'),
    ('2019-06-10 00:00:00.000', 'BANKDAY'),
    ('2019-06-11 00:00:00.000', 'BANKDAY'),
    ('2019-06-12 00:00:00.000', 'BANKDAY'),
    ('2019-06-13 00:00:00.000', 'BANKDAY'),
    ('2019-06-14 00:00:00.000', 'BANKDAY'),
    ('2019-06-15 00:00:00.000', 'SATURDAY'),
    ('2019-06-16 00:00:00.000', 'SUNDAY'),
    ('2019-06-17 00:00:00.000', 'BANKDAY'),
    ('2019-06-18 00:00:00.000', 'BANKDAY'),
    ('2019-06-19 00:00:00.000', 'BANKDAY'),
    ('2019-06-20 00:00:00.000', 'BANKDAY')
GO



-------  MyTable1 TABLE  --------------------------------------------------------------------

CREATE TABLE [dbo].[MyTable1](
    [ID] [int] NOT NULL,
    [rDate] [date] NOT NULL,
    [eTime] [decimal](12,6) NOT NULL,
    [rDura] [date] NULL
) 



INSERT INTO MyTable1 (ID, rDura, eTime, rDate) VALUES
    (17008431,NULL,0.1855,'2019-05-02'), 
    (17008477,NULL,0.059,'2019-05-02'), 
    (17008500,NULL,0.329667,'2019-05-02'), 
    (17090449,NULL,3.3195,'2019-05-02'), 
    (16888594,NULL,13.830667,'2019-04-26'), 
    (16888681,NULL,12.6635,'2019-04-26'), 
    (16888722,NULL,8.154667,'2019-05-07'), 
    (16888750,NULL,7.83,'2019-05-07'), 
    (16888766,NULL,5.22,'2019-05-07'), 
    (16955798,NULL,12.35,'2019-05-07'), 
    (17108201,NULL,1.669833,'2019-05-07'), 
    (17110834,NULL,2.596667,'2019-05-02'), 
    (17111001,NULL,0.814667,'2019-05-06'), 
    (16893842,NULL,1.053,'2019-05-07'), 
    (16951779,NULL,2.720833,'2019-05-03'), 
    (16951821,NULL,4.042333,'2019-05-06'), 
    (17017058,NULL,0.227333,'2019-05-02'), 
    (17017060,NULL,1.06,'2019-05-02'), 
    (17017066,NULL,1.869333,'2019-05-02'), 
    (17019289,NULL,0.835667,'2019-04-26'), 
    (17020295,NULL,3.983333,'2019-04-21'), 
    (17106404,105,3.3545,'2019-04-29'), 
    (17107843,NULL,2.815167,'2019-05-07'), 
    (16725584,NULL,0.693,'2019-04-25'), 
    (17101197,NULL,3.906667,'2019-04-30'), 
    (17101993,NULL,0.571667,'2019-05-06'), 
    (17102225,NULL,3.048833,'2019-04-30'), 
    (17102482,NULL,7.5945,'2019-05-10'), 
    (16974196,NULL,1.633333,'2019-05-06'), 
    (17113406,NULL,0.871833,'2019-05-02'), 
    (17113408,NULL,0.749833,'2019-05-02'), 
    (17113784,NULL,1.961333,'2019-05-03'), 
    (17120601,NULL,4.033333,'2019-05-06'), 
    (17120609,NULL,3.983333,'2019-05-06'), 
    (17120618,NULL,2.626667,'2019-05-06'), 
    (17120626,NULL,2.64,'2019-05-06'), 
    (17120628,NULL,3.684167,'2019-05-06'), 
    (17121720,NULL,2.235,'2019-04-30'), 
    (17058455,NULL,5.806667,'2019-04-29'), 
    (17059476,NULL,2.264833,'2019-05-22'), 
    (17059478,NULL,182.603667,'2019-05-30'), 
    (17065386,NULL,5.539667,'2019-05-10'), 
    (16927091,NULL,1.381,'2019-05-14'), 
    (16927093,NULL,112.304685,'2019-05-14'), 
    (16991456,NULL,0.931667,'2019-04-29'), 
    (17122394,NULL,1.560167,'2019-05-03'), 
    (17126711,NULL,4.046,'2019-05-03'), 
    (16935823,NULL,0.359,'2019-04-25'), 
    (17069727,NULL,1.952833,'2019-05-03'), 
    (17069870,NULL,1.742333,'2019-05-02'), 
    (17070555,NULL,5.416667,'2019-05-02'), 
    (17070557,NULL,3.894167,'2019-05-02'), 
    (17070851,NULL,2.64,'2019-04-23'), 
    (17073724,NULL,0.737667,'2019-05-03'), 
    (17074763,NULL,1.413833,'2019-05-02'), 
    (17131824,NULL,4.258,'2019-05-10'), 
    (17132133,NULL,0.257667,'2019-05-14'), 
    (17132865,NULL,2.769833,'2019-05-17'), 
    (17138082,NULL,7.866667,'2019-05-31'), 
    (17139196,NULL,5.860167,'2019-05-03'), 
    (17139200,NULL,1.479667,'2019-05-03'), 
    (16983337,NULL,2.951667,'2019-05-02'), 
    (17028542,NULL,0.680333,'2019-05-13'), 
    (16823160,NULL,5,'2019-05-06'), 
    (16823168,NULL,5,'2019-05-06'), 
    (16823182,NULL,5,'2019-05-06'), 
    (16823192,NULL,5,'2019-05-06'), 
    (16906776,NULL,0.8635,'2019-05-02'), 
    (17082286,NULL,3.333333,'2019-05-09'), 
    (17083776,NULL,2.317167,'2019-04-25'), 
    (17083778,NULL,1.447167,'2019-05-02'), 
    (17084568,NULL,0.2375,'2019-05-02'), 
    (17154415,NULL,2.64,'2019-05-14'), 
    (17154425,NULL,2.626667,'2019-05-14'), 
    (17154453,NULL,0.052,'2019-05-06'), 
    (17155029,NULL,3.256667,'2019-05-22'), 
    (17157159,NULL,1.333333,'2019-05-15'), 
    (16994233,NULL,0.252167,'2019-04-29'), 
    (17039767,NULL,1.401667,'2019-05-10'), 
    (17040346,NULL,4.021667,'2019-05-09'), 
    (17040815,NULL,1.2675,'2019-05-16'), 
    (17042063,NULL,0.213333,'2019-05-03'), 
    (17050144,NULL,0.976667,'2019-05-02'), 
    (17050150,NULL,0.837167,'2019-05-20'), 
    (17051422,NULL,1.826,'2019-05-07'), 
    (17142464,NULL,0.464333,'2019-05-06'), 
    (17145501,NULL,4.745333,'2019-06-06'), 
    (17145980,NULL,0.195167,'2019-05-07'), 
    (17145999,NULL,1.330833,'2019-05-07'), 
    (17146001,NULL,1.503833,'2019-05-06'), 
    (17146011,NULL,1.22,'2019-05-03'), 
    (17146017,NULL,0.373,'2019-05-07'), 
    (17146023,NULL,0.5745,'2019-05-03'), 
    (17146127,NULL,1.7835,'2019-05-15'), 
    (17146131,NULL,13.5595,'2019-05-07'), 
    (17152617,NULL,4.535667,'2019-05-10'), 
    (17154390,NULL,3.983333,'2019-05-14'), 
    (17154398,NULL,5.416667,'2019-05-14'), 
    (17154400,NULL,3.684167,'2019-05-14')

 GO

建议 ROW_NUMBER() 没有帮助解决问题。我必须将任务分为两个步骤:首先,我必须设置一个变量 @bDate 来存储 2 个内部嵌套 SELECT 的结果,然后在单独的 [=13] 中找到该日期的索引=]步.

DECLARE @eDate as Date
DECLARE @DayLimit INT
SET @DayLimit = 60  -- let's assume a constant here
SET @eDate = DATEFROMPARTS('2019','05','31')

-- get balance point INDEX over non-empty days    
SELECT TOP 1 @bDate = SRC2.rDate   -- get first balance point (date)
FROM
    (
    SELECT 
            ROW_NUMBER() OVER (ORDER BY SRC.rDate) as RowNo
        ,SRC.rDate 
        ,SRC.TotalPerDay      -- not required for processing, included just for info and check
        ,(SELECT (SUM((eTime-ISNULL(rDura,0))/60)) FROM MyTable1 as MT WHERE MT.r <= SRC.rDate AND MT.r < @eDate)         as CumulativeSum1
        ,((SELECT COUNT(cDate) FROM Calendar WHERE KindOfDay = 'BANKDAY' AND cDate BETWEEN GETDATE() AND SRC.rDate) * @DayLimit) as CumulativeSum2
    FROM (
        SELECT   
                CASE  
                    WHEN CAST(r as DATE) < CAST(GETDATE() as date)  
                    THEN DATEADD(dd,-1,CAST(GETDATE() as date))                
                    ELSE CAST(r as date)                           
                END as rDate, 
                SUM((eTime-ISNULL(rDura,0))/60) as TotalPerDay      
        FROM MyTable1 
        WHERE r < @eDate
        GROUP BY  -- group by non-empty dates, group all past dates to yesterday
                CASE  
                    WHEN CAST(r as DATE) < CAST(GETDATE() as date)  
                    THEN DATEADD(dd,-1,CAST(GETDATE() as date))               
                    ELSE CAST(r as date)                             
                END                                
    ) as SRC                          
    --ORDER BY rDate
    ) as SRC2  -- compiled list of sums per day
WHERE SRC2.CumulativeSum2 > SRC2.CumulativeSum1;    -- balance condition

-- get the index of bank day from Today
SELECT (SELECT COUNT(cDate) FROM Calendar WHERE KindOfDay = 'BANKDAY' AND cDate BETWEEN GETDATE() AND @bDate);