【问题标题】:Adding Missing Month in T-SQL在 T-SQL 中添加缺少的月份
【发布时间】:2017-08-28 09:14:59
【问题描述】:

我有一个类似这样的数据:

LoanId  PaymentDate PaymentMonth PaymentAmount
  L1     12-01-2008    01             100
  L2     15-02-2008    02             300
  L3     01-04-2008    04             500
  L3     01-10-2008    10             500  

我想为每个 LoanId 添加缺少的 PaymentMonth,如下所示:

    LoanId  PaymentYear PaymentMonth PaymentAmount
  L1           2008        01             100
  L1           2008        02              0
  L1           2008        03              0
  ..            ..         ..             ..
  L1           2008        12              0
  L2           2008        01              0
  L2           2008        02             300
  L2           2008        03              0
  ..            ..         ..             ..
  L3           2008        01              0
  L3           2008        02              0
  L3           2008        03              0
  L3           2008        04             500
  ..            ..         ..             ..
  L3           2008        10             500
  ..            ..         ..              ..
  L3           2008        12              0

以前是手动操作,但现在从 2008 年到 20012 年获得了超过 10 万个 LoanId

【问题讨论】:

标签: sql tsql datetime


【解决方案1】:

尝试这样做:

use db_test;
go

create table dbo.test1 
(
    loanId          varchar(2),
    paymentDate     date,
    paymentMonth    varchar(2),
    paymentAmount   float
);

set dateformat dmy;

insert into dbo.test1 
values 
    ('L1', '12-01-2008', '01', 100),
    ('L2', '15-02-2008', '02', 300),
    ('L3', '01-04-2008', '04', 500),
    ('L3', '01-10-2008', '10', 500);  

set dateformat ymd;

with cte as (
    select cast('2008-01-31' as date) as month_dt, 1 as month_nm, format(1, 'd2') as paymentMonth
    union all
    select eomonth(dateadd(month, 1, month_dt)), month_nm + 1, format(month(month_dt) % 12 + 1, 'd2') 
    from cte
    where month_dt < '2012-12-31'
), cte2 as (
    select 
        t.loanId, 
        x.month_dt,
        x.paymentMonth
    from (
        select distinct loanId from dbo.test1
    ) t
    join cte x 
        on 1 = 1
)
select
    a.loanId, year(a.month_dt) as paymentYear, a.paymentMonth, coalesce(b.sm, 0) as paymentAmount
from 
    cte2 a
    left join (
        select loanId, eomonth(paymentDate) as paymentDate, paymentMonth, sum(paymentAmount) as sm
        from dbo.test1
        group by loanId, eomonth(paymentDate), paymentMonth
    ) b
        on a.month_dt = b.paymentDate
        and a.loanId = b.loanId
order by 
    paymentYear asc,
    loanId asc,
    paymentMonth;

【讨论】:

  • 谢谢,效果很好 其他答案也有效,但需要很长时间或需要对整个数据框进行一些精确的编辑。这个使用起来非常简单
  • 如果您遇到递归 cte 问题,只需在查询末尾添加:OPTION (MAXRECURSION 32000)。
【解决方案2】:

你可以试试:

1.) 获取您的 MIN 和 MAX PaymentDate(我认为这些是您的范围)
2.) 在此范围内创建所有月份 - 在我的示例中使用公用表表达式)。
3.) 最后选择您的数据并加入这些月份日期并对结果进行分组

DECLARE @StartDate  DATETIME,
        @EndDate    DATETIME;

SET @StartDate = SELECT MIN(PaymentDate) 
                   FROM yourtable

SET @EndDate   = SELECT MAX(PaymentDate) 
                   FROM yourtable

;WITH CTE AS (
    SELECT DATEADD(MONTH, x.number, @StartDate) as Months
      FROM master.dbo.spt_values x
     WHERE x.type = 'P'        
       AND x.number <= DATEDIFF(MONTH, @StartDate, @EndDate)
)
  SELECT yourtable.LoanID
        ,yourtable.PaymentYear
        ,yourtable.PaymentMonth
        ,SUM(ISNULL(PaymentAmount,0)) as PaymentAmount
    FROM CTE
    INNER JOIN yourtable
      ON yourtable.PaymentYear = CONVERT(VARCHAR(4),DATEPART(YEAR, Months)) 
     AND yourtable.PaymentMonth = RIGHT('0' + CONVERT(VARCHAR(2),DATEPART(MONTH, Months)),2)
GROUP BY yourtable.LoanID
        ,yourtable.PaymentYear
        ,yourtable.PaymentMonth   

【讨论】:

    【解决方案3】:

    这是一种方法,非常简单。必要的 cmets 在代码中。

    declare @LoanData table (
    ID char(2),
    PaymentDate date,
    PaymentAmount int
    )
    insert into @LoanData values
    ('L1', '01-12-2008',100),
    ('L2', '02-15-2008',300),
    ('L3', '04-01-2008',500),
    ('L3', '10-01-2008',500)
    declare @TableID table(id char(2))
    --list of IDs
    insert into @TableID select distinct ID from @LoanData
    
    declare @PaymentMonth table(
    LoanID char(2),
    PaymentYear int,
    PaymentMonth int,
    PaymentAmount int
    )
    declare @month int, @year int, @i int, @id char(2)
    select @i = count(*) from @TableID
    
    --first get the table which has recotrd for every month for every id (default value in PaymentAmount is 0)
    while @i > 0
    begin
        select top 1 @id = id from @TableID
    
        set @year=2008
        while @year <= 2012
        begin
            set @month=1
            while @month <= 12
            begin
                insert into @PaymentMonth values (@id, @year, @month, 0)
                set @month = @month + 1
            end
    
            set @year = @year + 1
        end
        delete from @TableID where id = @id
        set @i = @i - 1
    end
    --update table based on your initial data
    update @PaymentMonth
    set PaymentAmount = A.PaymentAmount from @LoanData as A
    where LoanID = A.ID and PaymentYear = datepart(YEAR, A.PaymentDate) and PaymentMonth = datepart(MONTH, A.PaymentDate)
    
    select * from @PaymentMonth
    

    【讨论】:

      【解决方案4】:
      create table temp_loantable (
      loanid bigint,
      paymentdate date,
      paymentmonth varchar(2),
      paymentamount numeric(10,2))
      

      有一个日期范围(年和月),然后进行左外连接应该得到所需的输出。

      select * from 
      (select years.n yearval , months.n monthval
      from (values(2008), (2009), (2010), (2011), (2012)) years(n),
      (values(1),(2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)) months(n)) a
      left outer join temp_loantable l 
      on year(paymentdate) = a.yearval 
         and month(paymentdate) = a.monthval
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-12-19
        • 1970-01-01
        • 2017-07-23
        • 1970-01-01
        • 1970-01-01
        • 2020-05-07
        • 1970-01-01
        相关资源
        最近更新 更多