如何生成要插入现有行之间的行

时间:2019-07-17 11:52:59

标签: sql sql-server

在我们公司,对客户进行定期评估并为其评分。评估结果存储在数据库(SQL Server 2017)中,结果看起来像这样。

ClientID  AssessID AssessMonth RepMonth AssessGrade
1     100      01/2018     01/2018  0.98
1     149      07/2018     07/2018  0.80
1     199      01/2019     01/2019  0.45

到目前为止,太好了。现在,来自监管机构的办公室已收到要求为他们提供每个报告月每个客户的评估等级的要求。这意味着我们需要为以后的每个ReportingMonth重复第一个记录,直到完成新的评估为止(或者,如果没有新的评估,则继续重复该记录)。使结果看起来像这样:

ClientID AssessID AssessMonth   RepMonth AssessResult
1    100      01/2018   01/2018  0.98
1    100      01/2018   02/2018  0.98
1    100      01/2018   03/2018  0.98
1    100      01/2018   04/2018  0.98
1    100      01/2018   05/2018  0.98
1    100      01/2018   06/2018  0.98
1    149      07/2018   07/2018  0.80
1    149      07/2018   08/2018  0.80
1    149      07/2018   09/2018  0.80
1    149      07/2018   10/2018  0.80
1    149      07/2018   11/2018  0.80
1    149      07/2018   12/2018  0.80
1    199      01/2019   01/2019  0.45

您可能会看到重复每条记录,直到对该客户进行新评估为止。仅RepMonth会获得一个新值,该值比其前任值高一个。数据库中不存在介于两者之间的记录。

我可以生成一个表,其中包含所有可能的报告月份,但是我不知道如何获得期望的结果。

2 个答案:

答案 0 :(得分:1)

按以下方式工作是可行的(我已经使用了测试数据,并使用开始和结束月份进行了参数设置):

DECLARE
    @StartMonth date = '2019-01-01'
    , @EndMonth date = '2019-04-01'

DECLARE @t table
(
    ClientID int
    , AssessID int
    , AssessMonth date
    , RepMonth date
    , AssessGrade decimal(19, 2)
)

INSERT INTO @T
VALUES (1, 1, '2019-01-01', '2019-01-01', 0.5)

INSERT INTO @T
VALUES (1, 2, '2019-04-01', '2019-04-01', 0.9)

INSERT INTO @T
VALUES (2, 1, '2019-01-01', '2019-01-01', 0.4)

INSERT INTO @T
VALUES (2, 2, '2019-03-01', '2019-03-01', 0.3)
;

WITH cteMonths
AS
(
    SELECT CAST(@EndMonth AS date) RepMonth
    UNION ALL
    SELECT DATEADD(MONTH, -1, RepMonth)
    FROM cteMonths
    WHERE DATEADD(MONTH, -1, RepMonth) >= @StartMonth
)
,

cteMonthsClients
AS
(
    SELECT
        M.RepMonth
        , C.ClientID
    FROM
        cteMonths M 
        CROSS JOIN (SELECT DISTINCT ClientID FROM @t) C
)

SELECT
    M.ClientID
    , M.RepMonth
    , ISNULL(T.AssessID, T2.AssessID) AssessID
    , ISNULL(T.AssessMonth, T2.AssessMonth) AssessMonth
    , ISNULL(T.AssessGrade, T2.AssessGrade) AssessGrade
FROM
    cteMonthsClients M
    LEFT JOIN @t T ON
        M.RepMonth = T.RepMonth
        AND M.ClientID = T.ClientID
    OUTER APPLY
    (
        SELECT TOP 1
            AssessID
            , AssessMonth
            , AssessGrade
        FROM @t T2
        WHERE
            T2.ClientID = M.ClientID
            AND T2.RepMonth < M.RepMonth
        ORDER BY RepMonth DESC
    ) T2
ORDER BY
    ClientID
    , RepMonth

答案 1 :(得分:0)

这是另一种方法,您将必须使用日期维度表,该表将为您提供以下输出:

ClientID    AssessID    AssessMonth RepMonth    AssessGrade
1           100         01/2018     01/2018     0.98
1           100         01/2018     02/2018     0.98
1           100         01/2018     03/2018     0.98
1           100         01/2018     04/2018     0.98
1           100         01/2018     05/2018     0.98
1           100         01/2018     06/2018     0.98
1           149         07/2018     07/2018     0.80
1           149         07/2018     08/2018     0.80
1           149         07/2018     09/2018     0.80
1           149         07/2018     10/2018     0.80
1           149         07/2018     11/2018     0.80
1           149         07/2018     12/2018     0.80
1           199         01/2019     01/2019     0.45

有以下内容:

--Create Assessment Table 
DECLARE @Table1 AS TABLE 
(
    ClientID INT,
    AssessID INT,
    AssessMonth VARCHAR(MAX),
    RepMonth VARCHAR(MAX),
    AssessGrade NUMERIC(18,2)
)

INSERT INTO @Table1 VALUES 
(1,100,'01/2018','01/2018',0.98),
(1,149,'07/2018','07/2018',0.80),
(1,199,'01/2019','01/2019',0.45)

--Dummy date dimension table 
DECLARE @MonthYear AS TABLE 
(
    [Month] VARCHAR(10), 
    [Year] VARCHAR(10), 
    [MonthYear] AS [Month] +'/'+ [Year]
)

INSERT INTO @MonthYear VALUES 
('01',2017),('02',2017),('03',2017),('04',2017),('05',2017),('06',2017),('07',2017),('08',2017),('09',2017),('10',2017),('11',2017),('12',2017),
('01',2018),('02',2018),('03',2018),('04',2018),('05',2018),('06',2018),('07',2018),('08',2018),('09',2018),('10',2018),('11',2018),('12',2018),
('01',2019),('02',2019),('03',2019),('04',2019),('05',2019),('06',2019),('07',2019),('08',2019),('09',2019),('10',2019),('11',2019),('12',2019),
('01',2020),('02',2020),('03',2020),('04',2020),('05',2020)

--Final select statement 
SELECT 
    t.ClientID,
    t.AssessID, 
    t.AssessMonth,
    m.MonthYear AS RepMonth, 
    t.AssessGrade
FROM 
    @Table1 t
CROSS JOIN @MonthYear m
OUTER APPLY 
(
    SELECT TOP 1 
        AssessMonth
    FROM 
        @Table1 
    WHERE 
        AssessID > t.AssessID
    ORDER BY AssessID
) o 
OUTER APPLY 
(
    SELECT TOP 1 
        AssessMonth 
    FROM
        @Table1
    ORDER BY 
        AssessID DESC
) o2
WHERE 
    (CAST(RIGHT('0' + RIGHT(m.MonthYear, 4) + LEFT(m.MonthYear, 2), 6) AS INT) < CAST(RIGHT('0' + RIGHT(o.AssessMonth, 4) + LEFT(o.AssessMonth, 2), 6) AS INT) 
     OR (O2.AssessMonth = t.AssessMonth AND CAST(RIGHT('0' + RIGHT(m.MonthYear, 4) + LEFT(m.MonthYear, 2), 6) AS INT) <= CAST(RIGHT('0' + RIGHT(o2.AssessMonth, 4) + LEFT(o2.AssessMonth, 2), 6) AS INT)))
    AND CAST(RIGHT('0' + RIGHT(m.MonthYear, 4) + LEFT(m.MonthYear, 2), 6) AS INT) >= CAST(RIGHT('0' + RIGHT(t.AssessMonth, 4) + LEFT(t.AssessMonth, 2), 6) AS INT)
ORDER BY 
    t.ClientID, t.AssessID, M.Year, M.Month