如何在SQL Server中重复缺少日期的最后一行?

How to repeat rows of last date for missing date in SQL Server?

提问人:nizam uddin 提问时间:11/3/2023 最后编辑:Charliefacenizam uddin 更新时间:11/5/2023 访问量:51

问:

我在工作日有每个帐户的余额,我需要计算,但我需要计算一个月的所有日子,包括缺少数据的周末。我想重复缺少日期的帐户的最后可用余额。Avg_Balance

例如:

BDate 客户关系管理 ACCT的 BAL
2023-10-24 123 ab123型 1000
2023-10-25 123 ab123型 1100
2023-10-27 123 ab123型 1200
2023-10-28 123 ab123型 1300

如您所见,缺少 2023-10_26 的数据,我想复制 2023-10-25 的数据,但日期为 2023-10-26,如下所示。

BDate 客户关系管理 ACCT的 BAL
2023-10-24 123 ab123型 1000
2023-10-25 123 ab123型 1100
2023-10-26 123 ab123型 1100
2023-10-27 123 ab123型 1200
2023-10-28 123 ab123型 1300

如何在SQL Server查询中执行此操作?我将感谢您的帮助。

T-SQL SQL-SERVER-2012

评论

4赞 Charlieface 11/3/2023
你有日期表吗?
1赞 Panagiotis Kanavos 11/3/2023
SQL Server 2012 已过其生命周期结束日期。你真的有这个目标吗?在任何情况下,如果您确实有一个 Calendar 表,甚至有一个具有预先计算日期的表,则可以使用该函数按日期返回以前的值。日历/日期表将轻松生成缺失的日期。LAG(someField) OVER (ORDER BY BDate)

答:

0赞 PashaW 11/3/2023 #1

可以使用表变量和 while 循环实现查询。使用 MIN 和 MAX 获取您想要的计算日期范围,while 循环可以简单地检查相关日期是否存在数据,如果没有,则使用 DATEADD() 函数获取前几天的数据。我在下面列出了如何实现结果,但不得不对表格的结构做出一些假设。

--  Create a table variable to store the data...
DECLARE @CheckBalance AS TABLE
(
    BDate DATE NOT NULL,
    CRM VARCHAR(3) NOT NULL,
    ACCT VARCHAR(5) NULL,
    BAL INT
);

DECLARE @StartDate DATE, @EndDate DATE, @WorkingDate DATE;
--  Get the starting date and the end date you want to calculate...
--  Add an extra day to the end date for the while loop check.
SELECT @StartDate = MIN(BDate), @EndDate = DATEADD(DAY, 1, MAX(BDate)) FROM RunningBalance;

SELECT @WorkingDate = @StartDate;

WHILE @WorkingDate < @EndDate
BEGIN

    IF NOT EXISTS(SELECT 1 FROM RunningBalance WHERE BDate = @WorkingDate)
    BEGIN
        --  Since the data is missing from the WorkingDate, insert the previous days data.
        INSERT INTO @CheckBalance (BDate, CRM, ACCT, BAL)
            SELECT @WorkingDate AS BDate, CRM, ACCT, BAL FROM RunningBalance WHERE BDate = DATEADD(DAY, -1, @WorkingDate);
    END
    ELSE
    BEGIN
        --  Data exists, insert that.
        INSERT INTO @CheckBalance (BDate, CRM, ACCT, BAL)
            SELECT BDate, CRM, ACCT, BAL FROM RunningBalance WHERE BDate = @WorkingDate;
    END

    SELECT @WorkingDate = DATEADD(DAY, 1, @WorkingDate);

END

SELECT BDate, CRM, ACCT, BAL FROM @CheckBalance;

评论

0赞 digital.aaron 11/4/2023
虽然这在技术上是可行的,但它仍在处理和构建表 RBAR。使用 Dates 表和函数来制作基于集的查询解决方案是一个更好的主意。LAG()
1赞 jequ 11/4/2023 #2

假设您有一个日期维度表,您可以联接该表以获得一个包含间隙和孤岛的表。然后,将有多个选项来填补空白(NULL 值)。这是一个经典的:

首先,让我们建立你的示例:

CREATE TABLE RUNNUNG_BALANCE_TABLE (
  BDATE DATE NOT NULL,
  CRM INT,
  ACCT NVARCHAR(5),
  BAL INT
);

INSERT INTO RUNNUNG_BALANCE_TABLE
VALUES
  ('2023-10-24',123,'ab123',1000),
  ('2023-10-25',123,'ab123',1100),
  ('2023-10-27',123,'ab123',1200),
  ('2023-10-28',123,'ab123',1300);

CREATE TABLE DIM_DATE (
  DATEKEY INT NOT NULL,
  DATE_ISO DATE NOT NULL
);

INSERT INTO DIM_DATE
VALUES
  (20231024,'2023-10-24'),
  (20231025,'2023-10-25'),
  (20231026,'2023-10-26'),
  (20231027,'2023-10-27'),
  (20231028,'2023-10-28'),
  (20231029,'2023-10-29'),
  (20231030,'2023-10-30');

然后,我们使用公用表表达式 (CTE) 将财务数据表与包含日期值的表联接起来,并创建一个新列来标记条目组。我们通过使用带有 COUNT 的 OVER 子句作为聚合函数来做到这一点。我们使用此列来确定“最后可用”的数据。我们可以在这里使用 MIN(),因为任何值总是大于 NULL。

WITH CTE AS (
  SELECT 
    DATE_ISO,
    CRM,
    ACCT,
    BAL,
    COUNT(CASE WHEN BAL IS NOT NULL THEN 1 END) OVER (ORDER BY DATE_ISO) AS grp
  FROM RUNNUNG_BALANCE_TABLE
  RIGHT JOIN DIM_DATE ON DATE_ISO = BDATE
)
SELECT
  DATE_ISO AS BDATE,
  MIN(CRM) OVER (PARTITION BY grp) AS CRM,
  MIN(ACCT) OVER (PARTITION BY grp) AS ACCT,
  MIN(BAL) OVER (PARTITION BY grp) AS BAL
FROM CTE;

CTE 如下所示:

DATE_ISO 客户关系管理 ACCT的 BAL GRP公司
2023-10-24 123 ab123型 1000 1
2023-10-25 123 ab123型 1100 2
2023-10-26 (空) (空) (空) 2
2023-10-27 123 ab123型 1200 3
2023-10-28 123 ab123型 1300 4
2023-10-29 (空) (空) (空) 4
2023-10-30 (空) (空) (空) 4

最终结果如下所示:

BDATE公司 客户关系管理 ACCT的 BAL
2023-10-24 123 ab123型 1000
2023-10-25 123 ab123型 1100
2023-10-26 123 ab123型 1100
2023-10-27 123 ab123型 1200
2023-10-28 123 ab123型 1300
2023-10-29 123 ab123型 1300
2023-10-30 123 ab123型 1300

SQL 小提琴