【问题标题】:Is there an elegant way to calculate proportion of a year that lies within a date range in SQL Server 2014?是否有一种优雅的方法来计算 SQL Server 2014 中日期范围内的年份比例?
【发布时间】:2020-01-15 13:31:43
【问题描述】:

如上所述,我希望在我的数据集中创建一个列,说明日期范围内一年的百分比。

这已经在 excel 中完成,但我现在必须在 SQL 中重现它。以下是列数据类型:

  • Yr1 int
  • Yr2 浮动
  • Yr3 浮动
  • Yr4 浮动
  • 期间开始日期时间
  • 期间结束日期时间
  • 期间指示器 varchar
  • 闰年指示器 varchar
  • 周期浮动的长度
  • Yr1 赚取 % int
  • Yr2 赚取 % 浮动
  • 第三年赚取浮动百分比
  • 第四年赚取浮动百分比
  • 总收入百分比浮动

这是用于生成列的 SQL。不幸的是,Yr1 Earn % 不起作用。我收到一条错误消息:“操作数数据类型 datetime 对于除法运算符无效。”

有没有什么方法可以用下面的方式在 SQL 中计算这个??

      ,[Yr1]+1 as 'Yr2'
      ,[Yr2]+1 as 'Yr3'
      ,[Yr3]+1 as 'Yr4'
      ,dateadd(d,[INSRD PERIOD START],'1899-12-30') as 'Period Start'
      ,dateadd(d,[INSRD PERIOD END],'1899-12-30') as 'Period End'
      ,CASE WHEN MONTH([PERIOD START]) = MONTH([PERIOD END]) AND DAY([PERIOD START]) = DAY([PERIOD END])
            THEN 'Y'
            ELSE 'N'
       END AS 'Period Indicator'
      ,CASE WHEN ISDATE(CAST(YEAR([Period Start]) AS char(4)) + '0229') = 1 
                 OR 
                 ISDATE(CAST(YEAR([Period End]) AS char(4)) + '0229') = 1
            THEN 'Y' 
            ELSE 'N' 
       END as 'Leap Year Indicator'
      ,(([Period End]-([Period Start]-CASE WHEN [Period Indicator]='Y' 
                                          THEN 0 
                                          ELSE 1 
                                          END)))/(CASE WHEN [Leap Year Indicator]='N' 
                                                       THEN 365 
                                                       ELSE 366 
                                                       END) as 'Length of Period'
      ,(SELECT MAX(v) 
       FROM (VALUES (0),
        (((SELECT MIN(w) 
         FROM (VALUES ('2019-06-30'),
                      (CASE WHEN [Period Indicator]='N' 
                            THEN DATEADD(d,[Period End],'1899-12-30')
                            ELSE (DATEADD(DAY,-1,[Period End]))
                            END)
                       ,
                      (DATEFROMPARTS([Yr1],'12','31'))
              ) as value(w))-
                      (SELECT MAX(x) 
                       FROM (VALUES (DATEFROMPARTS([Yr1],'12','31')),
                                    (DATEADD(DAY,-1,[Period Start]))
                            ) as value(x)
                      )
        )/((CASE WHEN [Period Indicator]='N' 
                THEN dateadd(DAY,[Period End],'1899-12-30')
                ELSE DATEADD(DAY,-1,[Period End]) 
                END) 
                - (DATEADD(DAY,-1,[Period Start]))))
       ) as value(v)
       )  as 'Yr1 Earn %'
      ,[Yr2 Earn %] as 'Yr2 Earn %'
      ,[Yr3 Earn %] as 'Yr3 Earn %'
      ,[Yr4 Earn %] as 'Yr4 Earn %'
      ,[Yr1 Earn %]+[Yr2 Earn %]+[Yr3 Earn %]+[Yr4 Earn %] as 'Total Earn %'

编辑:这是用于在屏幕截图中生成 Yr1 Earn % 列的 excel 公式的副本。 z_eval='2019-06-30'

=MAX(0,(MIN(z_eval,IF($G2="N",$F2,$F2-1),DATE(A2,12,31))-MAX(DATE(A2->1, 12,31),$E2-1))/(IF($G2="N",$F2,$F2-1)-($E2-1))))

编辑:感谢大家迄今为止对 cme​​ts 的帮助。我使用Yr1 Earn % 的这段代码将所有dates 转换为ints。所有值都默认为零。外部 MAX(v) 函数是否难以解析内部 Max/Min 函数?

,(SELECT MAX(v) 
 FROM (VALUES (0),
  (((SELECT MIN(w) 
   FROM (VALUES (DATEDIFF(d,'1899-12-30','2019-06-30')),
                (CASE WHEN [Period Indicator]='N' 
                      THEN DATEDIFF(d,'1899-12-30',[Period End])
                      ELSE (DATEDIFF(d,'1899-12-30',DATEADD(DAY,-1,[Period End])))
                      END)
                 ,
                (DATEDIFF(d,'1899-12-30',DATEFROMPARTS([Yr1],'12','31')))
        ) as value(w))-
                (SELECT MAX(x) 
                 FROM (VALUES (DATEDIFF(d,'1899-12-30',DATEFROMPARTS([Yr1],'12','31'))),
                              (DATEDIFF(d,'1899-12-30',DATEADD(DAY,-1,[Period Start])))
                      ) as value(x)
                )
  )/((CASE WHEN [Period Indicator]='N' 
           THEN DATEDIFF(d,'1899-12-30',DATEADD(DAY,[Period End],'1899-12-30'))
           ELSE DATEDIFF(d,'1899-12-30',DATEADD(DAY,-1,[Period End])) 
           END) 
           - (DATEDIFF(d,'1899-12-30',DATEADD(DAY,-1,[Period Start])))))
 ) as value(v)
 ) as 'Yr1 Earn %'

【问题讨论】:

  • DATEDIFF 是你的朋友。顺便说一句,这也可以用来确定一年中的天数(如果是闰年,则间接确定,而不是相反)。
  • 如果您使用a Calendar table,这是一个简单的百分比计算。在 Date 上加入 Calendar 表,并计算范围内的日期数除以一年中的天数。
  • @Jeroen DATEDIFF 会很棒,但我的公式需要除法。 SQL 不允许我划分日期。我试过强制转换为 int 无济于事。
  • 确实,它不能划分日期,但DATEDIFF 以INT 的形式为您提供任何所需时间段内两个日期之间的差异,而这些日期确实可以划分。 Excel 将日期处理为代表天数加上分数的数字有点不稳定,并且可能难以转换为差异语义,但它总是可行的。 (只是不要让我当场做。)
  • 另一种方法仅显示一年中的完成百分比:=A1-DATE(YEAR(A1),1,0) 将显示一年中的第 # 天,除以 365(或 366 表示飞跃)得出一年的完成百分比。

标签: sql-server excel tsql


【解决方案1】:

当Select Max(v) FROM VALUES(....) as value(v) 像我上面所做的那样嵌套在选择语句中时,SQL 不能很好地发挥作用。不幸的是,我不得不将它们分解成单独的部分并将它们加入临时表中。这不是最优雅的解决方案,但它很可能是唯一没有连接的解决方案。列生成见下面的代码。

此外,将日期从 Excel 转换为 SQL 时会有 2 天的调整。

这进一步解释here:

select *,
        (1.000000000000000*(SELECT MIN(w) 
        FROM (VALUES (DATEDIFF(d,'1899-12-30',[EVAL])),
                    (CASE WHEN [Period Indicator]='N' 
                          THEN DATEDIFF(d,'1899-12-30',[Period End])
                          ELSE (DATEDIFF(d,'1899-12-30',DATEADD(DAY,-1,[Period End])))
                          END),
                    (DATEDIFF(d,'1899-12-30',DATEFROMPARTS([Yr1],'12','31')))
        ) as value(w))) as 'Yr1 Earn % a',
        (1.000000000000000*(SELECT MAX(x) 
            FROM (VALUES (DATEDIFF(d,'1899-12-30',DATEFROMPARTS([Yr1]-1,'12','31'))),
                         (DATEDIFF(d,'1900-01-01',DATEADD(DAY,-1,[Period Start])))) 
            as value(x))) as 'Yr1 Earn % b',
        (1.000000000000000*(SELECT MIN(w) 
        FROM (VALUES (DATEDIFF(d,'1899-12-30',[EVAL])),
                    (CASE WHEN [Period Indicator]='N' 
                          THEN DATEDIFF(d,'1899-12-30',[Period End])
                          ELSE (DATEDIFF(d,'1899-12-30',DATEADD(DAY,-1,[Period End])))
                          END),
                    (DATEDIFF(d,'1899-12-30',DATEFROMPARTS([Yr2],'12','31')))
        ) as value(w))) as 'Yr2 Earn % a',
        (1.000000000000000*(SELECT MAX(x) 
            FROM (VALUES (DATEDIFF(d,'1899-12-30',DATEFROMPARTS([Yr2]-1,'12','31'))),
                         (DATEDIFF(d,'1900-01-01',DATEADD(DAY,-1,[Period Start])))) 
            as value(x))) as 'Yr2 Earn % b',
        (1.000000000000000*(SELECT MIN(w) 
        FROM (VALUES (DATEDIFF(d,'1899-12-30',[EVAL])),
                    (CASE WHEN [Period Indicator]='N' 
                          THEN DATEDIFF(d,'1899-12-30',[Period End])
                          ELSE (DATEDIFF(d,'1899-12-30',DATEADD(DAY,-1,[Period End])))
                          END),
                    (DATEDIFF(d,'1899-12-30',DATEFROMPARTS([Yr3],'12','31')))
        ) as value(w))) as 'Yr3 Earn % a',
        (1.000000000000000*(SELECT MAX(x) 
            FROM (VALUES (DATEDIFF(d,'1899-12-30',DATEFROMPARTS([Yr3]-1,'12','31'))),
                         (DATEDIFF(d,'1900-01-01',DATEADD(DAY,-1,[Period Start])))) 
            as value(x))) as 'Yr3 Earn % b' ,
        (1.000000000000000*(SELECT MIN(w) 
        FROM (VALUES (DATEDIFF(d,'1899-12-30',[EVAL])),
                    (CASE WHEN [Period Indicator]='N' 
                          THEN DATEDIFF(d,'1899-12-30',[Period End])
                          ELSE (DATEDIFF(d,'1899-12-30',DATEADD(DAY,-1,[Period End])))
                          END),
                    (DATEDIFF(d,'1899-12-30',DATEFROMPARTS([Yr4],'12','31')))
        ) as value(w))) as 'Yr4 Earn % a',
        (1.000000000000000*(SELECT MAX(x) 
            FROM (VALUES (DATEDIFF(d,'1899-12-30',DATEFROMPARTS([Yr4]-1,'12','31'))),
                         (DATEDIFF(d,'1900-01-01',DATEADD(DAY,-1,[Period Start])))) 
            as value(x))) as 'Yr4 Earn % b',
        (1.000000000000000*(CASE WHEN [Period Indicator]='N' 
                 THEN DATEDIFF(d,'1900-01-01',[Period End])
                 ELSE DATEDIFF(d,'1900-01-01',DATEADD(DAY,-1,[Period End])) 
                 END)) as 'Earn % c',
        (1.000000000000000*(DATEDIFF(d,'1900-01-01',DATEADD(DAY,-1,[Period Start]))))as 'Earn % d'
into #temptemp2
from #temptemp1

【讨论】:

    猜你喜欢
    • 2014-02-13
    • 1970-01-01
    • 1970-01-01
    • 2023-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多