【问题标题】:SQL insert rows between existing recordsSQL 在现有记录之间插入行
【发布时间】:2017-02-02 06:37:54
【问题描述】:

我有一张表,其中包含仅在贷款和偿还的特定日期与贷款相对应的记录 - 即每笔贷款有两条记录。

Loans

Loan ID | Month     | Rate  | Action
--------------------------------
1       | 5/1/2000  | 5.50% | New
1       | 9/1/2000  | 6.00% | Closed
2       | 3/1/2000  | 5.25% | New
2       | 5/2/2000  | 5.50% | Closed

我有另一个记录,每个日期的利率。

Interest rates

Month       | Rate
--------------------
1/1/2000    | 5.00%
2/1/2000    | 5.25%
3/1/2000    | 5.25%
4/1/2000    | 5.25%
5/1/2000    | 5.50%
6/1/2000    | 5.50%
7/1/2000    | 5.50%
8/1/2000    | 6.00%
9/1/2000    | 6.00%
10/1/2000   | 6.50%

我想在贷款表中插入缺失日期的行(以及相应的利率),在给定贷款被取出和偿还之间。鉴于需要为每笔贷款插入中间日期,我无法通过 LEFT JOIN、MERGE 等解决此问题。

预期结果:

Loan ID | Month     | Rate  | Action
---------------------------------
1       | 5/1/2000  | 5.50% | New
1       | 6/1/2000  | 5.50% | NULL
1       | 7/1/2000  | 5.50% | NULL
1       | 8/1/2000  | 6%    | NULL
1       | 9/1/2000  | 6%    | Closed
2       | 3/1/2000  | 5.25% | New
2       | 4/1/2000  | 5.25% | NULL
2       | 5/1/2000  | 5.50% | Closed

【问题讨论】:

  • 请展示一些示例数据、预期输出并标记您正在使用的数据库。
  • 通过改进格式问题更具可读性。而且我改了一点用户的解释,我觉得更有意义。

标签: sql


【解决方案1】:

当您需要为一系列日期生成行时,数字表通常很有用。它只是一个包含大量整数的表。您可以阅读有关如何创建here(以及其他地方)的信息。一旦你有了它,你可以使用这样的查询(这是针对 SQL Server 的):

;with cteLoan as(
select t1.LoanID, min(t1.Month) StartDate, max(t1.Month) EndDate
from Table1 t1
group by t1.LoanID
)

select c.LoanID, dateadd(month, n.Number, c.StartDate) Month, t2.Rate, null Action
from Numbers n
cross join cteLoan c
join Table2 t2 on t2.Month = dateadd(month, n.Number, c.StartDate)
where n.Number >= 0 and n.number < DATEDIFF(month, c.StartDate, c.EndDate) 

union

select t1.LoanID, t1.Month, t1.Rate, t1.Action
from Table1 t1
order by LoanID, Month

要将结果插入另一个表,您应该能够:

insert into Results(LoadID, Month, Rate, Action)
select c.LoanID, dateadd(month, n.Number, c.StartDate) Month, t2.Rate, null Action
from Numbers n
cross join cteLoan c
join Table2 t2 on t2.Month = dateadd(month, n.Number, c.StartDate)
where n.Number >= 0 and n.number < DATEDIFF(month, c.StartDate, c.EndDate) 

union

select t1.LoanID, t1.Month, t1.Rate, t1.Action
from Table1 t1
order by LoanID, Month

【讨论】:

  • 杰拉德,你的建议奏效了——谢谢!但是我无法将输出保存为永久表。你能告诉我在哪里放置“SELECT * INTO ...”子句吗?谢谢。
  • 我尝试了上述方法,但收到以下错误:Msg 208,Level 16,State 1,Line 2 Invalid object name 'Results'。我需要先创建一个名为 Results 的表吗?
  • 你需要先创建结果表。您也可以将into Results 放在第一个SELECT 之后,以便即时插入新表。 select c.LoanID, dateadd(month, n.n, c.StartDate) Month, t2.Rate, null Action into Results ...
【解决方案2】:

您的设计很难,但您没有说明您使用的是什么数据库。这是一个 SQL 服务器示例(查询也适用于 postgreSQL):

DECLARE @loans TABLE
    (
      LoanID INT ,
      [Month] DATETIME ,
      Rate DECIMAL(4, 2) ,
      [Action] VARCHAR(10)
    );

INSERT  @loans
        ( LoanID, Month, Rate, Action )
VALUES  ( 1, '5/1/2000', 5.50, 'New    ' ),
        ( 1, '9/1/2000', 6.00, 'Closed ' ),
        ( 2, '3/1/2000', 5.25, 'New    ' ),
        ( 2, '5/2/2000', 5.50, 'Closed ' );


DECLARE @rates TABLE
    (
      Month DATETIME ,
      Rate DECIMAL(4, 2)
    );
INSERT  @rates
        ( Month, Rate )
VALUES  ( '1/1/2000', 5.00 ),
        ( '2/1/2000', 5.25 ),
        ( '3/1/2000', 5.25 ),
        ( '4/1/2000', 5.25 ),
        ( '5/1/2000', 5.50 ),
        ( '6/1/2000', 5.50 ),
        ( '7/1/2000', 5.50 ),
        ( '8/1/2000', 6.00 ),
        ( '9/1/2000', 6.00 ),
        ( '10/1/2000', 6.50 );

WITH    loanStart
          AS ( SELECT   *
               FROM     @loans
               WHERE    Action = 'New'
             ),
        loanEnd
          AS ( SELECT   *
               FROM     @loans
               WHERE    Action = 'Closed'
             ),
        loanRanges ( loanId, lStart, lEnd )
          AS ( SELECT   ls.LoanID ,
                        ls.Month ,
                        le.Month
               FROM     loanStart ls
                        INNER JOIN loanEnd le ON ls.LoanID = le.LoanID
             )
    SELECT  lr.loanId ,
            r.Month ,
            r.Rate ,
            COALESCE(ls.Action, le.Action) AS action
    FROM    @rates r
            INNER JOIN loanRanges lr ON r.Month >= lr.lStart
                                        AND r.Month <= lr.lEnd
            LEFT JOIN loanStart ls ON ls.LoanID = lr.loanId
                                      AND YEAR(ls.Month) = YEAR(r.Month)
                                      AND MONTH(ls.Month) = MONTH(r.Month)
            LEFT JOIN loanEnd le ON le.LoanID = lr.loanId
                                    AND YEAR(le.Month) = YEAR(r.Month)
                                    AND MONTH(le.Month) = MONTH(r.Month)
    ORDER BY lr.loanId ,
            r.Month;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-09
    • 2011-11-03
    相关资源
    最近更新 更多