sql - 给定示例结束日期和季度编号,计算日期的季度

标签 sql sql-server datetime finance

我遇到一个问题,我需要确定财务季度,但并不总是知道季度的开始/结束日期。然而,它们总是 3 个月长。我所知道的是当前季度的结束日期,以及所指的季度和年份。例如,我可能会得到:

当前季度:第四季度
当前年份:2021
当前季度结束日期:2021 年 1 月 31 日

如何获取其他日期的季度?如果这 3 个值中的任何一个发生更改,查询仍然需要根据这 3 个参数提供任何给定日期的季度。

我想出了以下方法,将过去 4 年的数据放入临时表中:

DECLARE @QuarterEnd             DATE            =   '1/31/2022'
,       @CurrentQuarter         INT             =   1
,       @CurrentYear            INT             =   2022
,       @Counter                INT             =   16
,       @qs                     INT             =   0
,       @qe                     INT             =   2
,       @DateToTest             DATE            =   '12/15/2021'

CREATE TABLE #Quarters (
        StartDate   DATE
,       EndDate     DATE
,       Qtr         INT
,       Yr          INT
)

WHILE @Counter <> 0
BEGIN
    INSERT INTO #Quarters VALUES (
            cast(DATEADD(MONTH, DATEDIFF(MONTH, 0, @QuarterEnd)-@qe , 0) as date)
        ,   cast(DATEADD(MONTH, DATEDIFF(MONTH, -1, @QuarterEnd)-@qs, -1) as date)
        ,   @CurrentQuarter
        ,   @CurrentYear
    )
    SET @Counter = @Counter - 1
    SET @qs = @qs + 3
    SET @qe = @qe + 3
    SET @CurrentQuarter = CASE WHEN @CurrentQuarter = 1 THEN 4 ELSE @CurrentQuarter - 1 END
    SET @CurrentYear = CASE WHEN @CurrentQuarter = 4 THEN @CurrentYear - 1 ELSE @CurrentYear END
END

SELECT  @DateToTest
,       (SELECT CONCAT('Q', Qtr, ' ', Yr) FROM #Quarters WHERE @DateToTest BETWEEN StartDate and EndDate)
FROM    #Quarters

但是,当我运行将返回数十万条记录的查询时,这似乎并不实用。

我想我可以将其放入一个函数中并使用以下方式调用它:

SELECT MyQuarter = dbo.MyQuarterFunction(@QuarterEnd, @CurrentQuarter, @CurrentYear, @DateToTest)

必须有一种更有效的方法来做到这一点。有什么建议吗?

最佳答案

只需创建一个名为 Quarters 的永久表即可。

CREATE TABLE dbo.Quarters
(
  StartDate     date,
  QuarterNumber tinyint,
  FiscalYear    int,
  NextQuarterStartDate AS (DATEADD(MONTH, 3, StartDate))
);

INSERT dbo.Quarters(StartDate, QuarterNumber, FiscalYear)
  VALUES('20200201',1,2020),
        ('20200501',2,2020),
        ('20200801',3,2020),
        ('20201101',4,2020),
        ('20210201',1,2021),
        ('20210501',2,2021),
        ('20210801',3,2021),
        ('20211101',4,2021),
        ('20220201',1,2022),
        ('20220501',2,2022),
        ('20220801',3,2022),
        ('20221101',4,2022);

现在,只要给您一个日期(例如 GETDATE()),您就可以轻松找到其他信息:

DECLARE @date date = GETDATE();

SELECT * FROM dbo.Quarters 
  WHERE @date >= StartDate
  AND @date < NextQuarterStartDate;

如果您需要同时支持多个会计日历,只需添加一列(例如 CalendarIDCompanyIDCustomerID)。

实际上,您甚至不需要日历或季度表。你已经有一 table 客户了,对吧?只需添加一列来存储其财政年度开始的月份。这确实是您所需要的。

CREATE TABLE dbo.Clients
(
  ClientID        int           NOT NULL CONSTRAINT PK_Clients PRIMARY KEY,
  Name            nvarchar(200) NOT NULL CONSTRAINT UQ_ClientName UNIQUE,
  FiscalYearStart tinyint       NOT NULL CONSTRAINT CK_ValidMonth 
    CHECK (FiscalYearStart BETWEEN 1 AND 12)
);

现在让我们插入一些包含不同会计年度的客户的几行:

INSERT dbo.Clients(ClientID, Name, FiscalYearStart)
  VALUES(1, N'ClientFeb',    2), -- fiscal year starts in February 
        (2, N'ClientMay',    5), -- fiscal year starts in May
        (3, N'ClientNormal', 1); -- fiscal year matches calendar

现在,是的,我们需要一个函数,但我们不要执行任何 while 循环、计数器或 #temp 表。

CREATE FUNCTION dbo.GetLast16Quarters
(
  @DateToTest date,
  @ClientID   int
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN 
(
  WITH n(n) AS
  (
    SELECT n = 1 UNION ALL 
    SELECT n + 1 FROM n WHERE n < 20
  ),
  Last20Quarters(QuarterStart, FiscalYearStart) AS
  (
    SELECT QuarterStart = DATEADD(QUARTER, 1-n, 
      DATEFROMPARTS(YEAR(@DateToTest)+1, FiscalYearStart, 1)), 
      FiscalYearStart
    FROM dbo.Clients CROSS JOIN n WHERE ClientID = @ClientID
  ),
  Last16Quarters AS
  (
    SELECT TOP (16) QuarterStart, 
      y = YEAR(DATEADD(MONTH, 1-FiscalYearStart, QuarterStart)) 
    FROM Last20Quarters WHERE QuarterStart < @DateToTest
    ORDER BY QuarterStart DESC
  )
  SELECT QuarterStart, 
         QuarterEnd = EOMONTH(QuarterStart, 2),
         FiscalYear = y,
         QuarterNumber = ROW_NUMBER() OVER 
           (PARTITION BY y ORDER BY QuarterStart)
  FROM Last16Quarters);

然后调用它:

DECLARE @DateToTest date = '20211215';
SELECT * FROM dbo.GetLast16Quarters(@DateToTest, 1);

输出:

QuarterStart QuarterEnd FiscalYear QuarterNumber
2018-02-01 2018-04-30 2018 1
2018-05-01 2018-07-31 2018 2
2018-08-01 2018-10-31 2018 3
2018-11-01 2019-01-31 2018 4
2019-02-01 2019-04-30 2019 1
2019-05-01 2019-07-31 2019 2
2019-08-01 2019-10-31 2019 3
2019-11-01 2020-01-31 2019 4
2020-02-01 2020-04-30 2020 1
2020-05-01 2020-07-31 2020 2
2020-08-01 2020-10-31 2020 3
2020-11-01 2021-01-31 2020 4
2021-02-01 2021-04-30 2021 1
2021-05-01 2021-07-31 2021 2
2021-08-01 2021-10-31 2021 3
2021-11-01 2022-01-31 2021 4

关于sql - 给定示例结束日期和季度编号,计算日期的季度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71444759/

相关文章:

sql - 根据长度在oracle sql中拆分字符串

java - JAVA 中的格式化文本字段和日期时间

sql - Postgres 并发性和可串行化。我需要 SERIALIZABLE 隔离级别吗?

c# - 未选择字段的SQL语句

sql-server - 当 WHERE 子句中只有单个列时,SQL Server 是否会使用复合索引?

sql-server - FN_TRACE_GETTABLE 上的 SQL Server EXECUTE AS

datetime - 如何计算两个不同的日期,例如 2 年零 5 个月

c# - ar-sa 文化的预期日期时间字符串是什么?

SQL 连接单个表的多行

sql - 如何同时选择 count(*) group by 和 select *?