递归更新id

时间:2012-02-27 20:06:04

标签: sql sql-server-2005 join

create table #customer (
id int not null primary key identity,
cust_no varchar(12),
meter_no varchar(10),
startdate smalldatetime,
enddate smalldatetime,
terminateDate smalldatetime,
oldid int null
)

insert into #customer values('AA111222','1111','2008-01-01', '2009-03-01','2008-04-15',null) 
insert into #customer values('AA111222','1111','2008-01-01', '2009-05-01',null,null) 
insert into #customer values('AA111222','1111','2008-03-01', '2008-12-01',null,null) 
insert into #customer values('AA111222','1111','2009-05-01', '2009-07-01',null,null) 
insert into #customer values('AA111222','1111','2009-08-01', '2009-11-01',null,null) 
insert into #customer values('AA111222','1111','2010-01-01', '2010-04-01',null,null) 
insert into #customer values('AA111222','1111','2010-07-01', '2011-07-01',null,null) 
insert into #customer values('AA111222','1111','2011-03-01', '2011-07-01',null,null) 
insert into #customer values('AA111222','1111','2011-07-01', '2012-07-01',null,null) 
insert into #customer values('BB111222','1112','2011-03-01', '2011-07-01',null,null) 
insert into #customer values('BB111222','1112','2011-07-01', '2012-07-01',null,null) 
insert into #customer values('CC111222','1113','2011-09-01', '2012-07-01',null,null) 
insert into #customer values('CC111222','1113','2011-03-01', '2011-07-01',null,null) 
insert into #customer values('CC111222','1113','2011-07-01', '2012-07-01',null,null) 

select * from #customer

场景:我们有超过10,000行。有些客户续费,其他则没有。我们想要构建层次结构

问题:根据oldIDcust_no共同提供标识特定客户的唯一组合这一事实,更新所有列的meter_no列。

行1,2,3中需要的主要帮助都具有相同的cust_no, meter_nostart_date,这意味着它们属于同一个客户。由于startdate是相同的,我们必须查看terminateDate,如果两行具有相同的起始数据并且其中一行终止,则终止的行首先出现在层次结构中,另一行出现在后面

下面

  • 第3行oldid应为1
  • 第1行oldid应为2
  • 第2行oldid应为null
  • 其余的几乎是按时间顺序排列的。

我尝试了这个问题,这对我的旧问题Update oldID for the records recursively有效,但我被困在这里。在这个上花了不少时间。

 Update #customer
 SET oldid =
        (Select TOP 1 c_old.id from #customer c_old
          where c_old.startdate <= #customer.startdate
          and c_old.cust_no = #customer.cust_no
          and c_old.meter_no = #customer.meter_no
          and c_old.id != #customer.id
          and #customer.oldid is null
          order by c_old.startdate desc,c_old.terminateDate desc
          )
  from #customer

我只是将数据转移到模型附近。

3 个答案:

答案 0 :(得分:2)

以下是整个查询(包括上面的基本代码)。根据我与鳄鱼的聊天,结果完全符合要求。这个解决方案没有先前的递归,因为garreth指出它是不必要的。我确实在下面保留了递归解决方案,以防问题变得更复杂并且需要实际的递归。两种解决方案都应该有效,但

create table #customer (
    id int not null primary key identity,
    cust_no varchar(12),
    meter_no varchar(10),
    startdate smalldatetime,
    enddate smalldatetime,
    terminateDate smalldatetime,
    oldid int null
)

insert into #customer values('AA111222','1111','2008-01-01', '2009-03-01','2008-04-15',null) 
insert into #customer values('AA111222','1111','2008-01-01', '2009-05-01',null,null) 
insert into #customer values('AA111222','1111','2008-03-01', '2008-12-01',null,null) 
insert into #customer values('AA111222','1111','2009-05-01', '2009-07-01',null,null) 
insert into #customer values('AA111222','1111','2009-08-01', '2009-11-01',null,null) 
insert into #customer values('AA111222','1111','2010-01-01', '2010-04-01',null,null) 
insert into #customer values('AA111222','1111','2010-07-01', '2011-07-01',null,null) 
insert into #customer values('AA111222','1111','2011-03-01', '2011-07-01',null,null) 
insert into #customer values('AA111222','1111','2011-07-01', '2012-07-01',null,null) 
insert into #customer values('BB111222','1112','2011-03-01', '2011-07-01',null,null) 
insert into #customer values('BB111222','1112','2011-07-01', '2012-07-01',null,null) 
insert into #customer values('CC111222','1113','2011-09-01', '2012-07-01',null,null) 
insert into #customer values('CC111222','1113','2011-03-01', '2011-07-01',null,null) 
insert into #customer values('CC111222','1113','2011-07-01', '2012-07-01',null,null) 

; WITH RankingCTE (id, cust_no, meter_no, startdate, enddate, terminatedate, 
        oldid, CustomerRank)
AS
(
    SELECT *, ROW_NUMBER() OVER 
        (PARTITION BY cust_no, meter_no ORDER BY startdate, terminatedate) 
                    AS CustomerRank
    FROM #customer
)
UPDATE #customer
SET oldid = OrganizedCTE.OldID
FROM #customer
    JOIN
        (   SELECT  BaseCTE.ID, NextInRankCTE.ID AS OldID
            FROM    RankingCTE AS BaseCTE
                    LEFT JOIN RankingCTE AS NextInRankCTE
                        ON NextInRankCTE.Meter_No = BaseCTE.Meter_No
                            AND NextInRankCTE.Cust_No = BaseCTE.Cust_no
                            AND BaseCTE.CustomerRank = NextInRankCTE.CustomerRank + 1
        ) AS OrganizedCTE
            ON OrganizedCTE.ID = #customer.ID
;

SELECT * FROM #customer

这是递归解决方案,还有完整的解决方案:

create table #customer (
    id int not null primary key identity,
    cust_no varchar(12),
    meter_no varchar(10),
    startdate smalldatetime,
    enddate smalldatetime,
    terminateDate smalldatetime,
    oldid int null
)

insert into #customer values('AA111222','1111','2008-01-01', '2009-03-01','2008-04-15',null) 
insert into #customer values('AA111222','1111','2008-01-01', '2009-05-01',null,null) 
insert into #customer values('AA111222','1111','2008-03-01', '2008-12-01',null,null) 
insert into #customer values('AA111222','1111','2009-05-01', '2009-07-01',null,null) 
insert into #customer values('AA111222','1111','2009-08-01', '2009-11-01',null,null) 
insert into #customer values('AA111222','1111','2010-01-01', '2010-04-01',null,null) 
insert into #customer values('AA111222','1111','2010-07-01', '2011-07-01',null,null) 
insert into #customer values('AA111222','1111','2011-03-01', '2011-07-01',null,null) 
insert into #customer values('AA111222','1111','2011-07-01', '2012-07-01',null,null) 
insert into #customer values('BB111222','1112','2011-03-01', '2011-07-01',null,null) 
insert into #customer values('BB111222','1112','2011-07-01', '2012-07-01',null,null) 
insert into #customer values('CC111222','1113','2011-09-01', '2012-07-01',null,null) 
insert into #customer values('CC111222','1113','2011-03-01', '2011-07-01',null,null) 
insert into #customer values('CC111222','1113','2011-07-01', '2012-07-01',null,null) 

SELECT *, ROW_NUMBER() OVER 
    (PARTITION BY cust_no, meter_no ORDER BY startdate ASC, terminatedate ASC) 
                AS CustomerRank
INTO #RankingTable
FROM #customer

;WITH SortingCTE(id, cust_no, meter_no, startdate, enddate, terminatedate, 
        oldid, CustomerRank)
AS
(
-- Anchor member definition
    SELECT id, cust_no, meter_no, startdate, enddate, terminatedate, 
                null as oldid, CustomerRank
    FROM #RankingTable
    WHERE CustomerRank = 1
    UNION ALL
-- Recursive member definition
    SELECT #RankingTable.id, #RankingTable.cust_no, #RankingTable.meter_no,
                #RankingTable.startdate, #RankingTable.enddate,
                #RankingTable.terminatedate, SortingCTE.id as oldid, 
                #RankingTable.CustomerRank
    FROM #RankingTable 
        JOIN SortingCTE 
            ON SortingCTE.cust_no = #RankingTable.cust_no
                AND SortingCTE.meter_no = #RankingTable.meter_no
                AND SortingCTE.CustomerRank+1 = #RankingTable.CustomerRank  
)
-- Statement that executes the CTE
UPDATE #customer
SET oldid = SortingCTE.oldid
FROM SortingCTE
    JOIN #customer on #customer.id = SortingCTE.id
;

SELECT * FROM #customer

答案 1 :(得分:2)

我不认为递归是必要的,因为每个ID只回复到前一个记录,而不是最旧的ID。

;WITH CTE AS
(   SELECT  *, ROW_NUMBER() OVER(PARTITION BY Cust_no, Meter_No ORDER BY StartDate, TerminateDate) [RowNum]
    FROM    #Customer
)
UPDATE  #Customer
SET     OldID = cte.OldID
FROM    #Customer c
        INNER JOIN
        (   SELECT  a.ID, b.ID [OldID]
            FROM    CTE a
                    LEFT JOIN CTE b
                        ON b.Meter_No = a.Meter_No
                        AND a.Cust_No = b.Cust_no
                        AND a.RowNum = b.RowNum + 1
        ) cte
            ON cte.ID = c.ID

CTE并不是必需的,子查询同样可以正常工作,但是当我使用相同的子查询两次或更多时,我倾向于使用CTE。

答案 2 :(得分:2)

我认为@GarethD's solution可以像这样简化:

;
WITH CTE AS (
  SELECT
    *,
    RowNum = ROW_NUMBER() OVER (
      PARTITION BY Cust_no, Meter_No
      ORDER BY StartDate, TerminateDate
    )
  FROM #Customer
)
UPDATE CTE
SET OldID = (
  SELECT ID
  FROM CTE a
  WHERE CTE.Cust_no  = a.Cust_no
    AND CTE.Meter_no = a.Meter_no
    AND CTE.RowNum   = a.RowNum + 1
)