SQL Server:将多行数据合并为一行

SQL Server:将多行数据合并为一行,sql,sql-server,sql-server-2008,tsql,ssis,Sql,Sql Server,Sql Server 2008,Tsql,Ssis,我要做的是合并多行数据,以便在Transact-SQL或SSIS中显示为一行。例如: 制作: 为此: REF ID Title Surname Forename DOB Add1 Postcode ---------------------------------------------------------------------------------- D 10 MRS KINGSTON BOB

我要做的是合并多行数据,以便在Transact-SQL或SSIS中显示为一行。例如:

制作:

为此:

REF  ID   Title Surname    Forename   DOB          Add1            Postcode
----------------------------------------------------------------------------------    
D    10   MRS   KINGSTON   BOB        15/07/1975   3 WATER SQUARE  TW13 7DT
因此,我所做的是将值合并在一起,忽略空值。(D=数据;T=更新)

欢迎提出任何建议


谢谢。

这将起作用,但由于没有标识或日期时间列,因此无法找到哪个更新行较新。因此,如果同一列上有更多的更新,我只需要按字母/数字(MIN)排列第一列

如果可以添加标识列,我们可以重写CTE部分以使其更准确

编辑:

若我们有标识列,并且CTE被重写为递归的,那个么实际上可以删除查询的整个其他部分

WITH CTE_RN AS 
(
    --Assigning row_Numbers based on identity - it has to be done since identity can always have gaps which would break the recursion
    SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY IDNT DESC) RN FROM dbo.Table2
)
,RCTE AS 
(
    SELECT  ID ,
            Title ,
            Surname ,
            Forename ,
            DOB ,
            Add1 ,
            Postcode ,
            RN FROM CTE_RN WHERE RN = 1 -- taking the last row for each ID
    UNION ALL
    SELECT r.ID,
        COALESCE(r.TItle,p.TItle), --Coalesce will hold prev value if exist or use next one
        COALESCE(r.Surname,p.Surname),
        COALESCE(r.Forename,p.Forename),
        COALESCE(r.DOB,p.DOB),
        COALESCE(r.Add1,p.Add1),
        COALESCE(r.Postcode,p.Postcode),
        p.RN
    FROM RCTE r
    INNER JOIN CTE_RN p ON r.ID = p.ID AND r.RN + 1 = p.RN --joining the previous row for each id
)
,CTE_Group AS 
(
    --rcte now holds both merged and unmerged rows, merged is max(rn)
    SELECT ID, MAX(RN) RN FROM RCTE
    GROUP BY ID  
)
SELECT r.* FROM RCTE r
INNER JOIN CTE_Group g ON r.ID = g.ID AND r.RN = g.RN

您可以尝试使用光标执行此操作:

BEGIN
  declare @Title sometype, @Surname sometype, @Forename sometype, @DOB sometype, @Add1 sometype, @Postcode sometype --vars to fetch the crusor into
  declare @rTitle sometype, @rSurname sometype, @rForename sometype, @rDOB sometype, @rAdd1 sometype, @rPostcode sometype --vars to keep the result

  DECLARE mycur CURSOR FOR  
    SELECT Title,Surname,Forename,DOB,Add1,Postcode
      FROM t1
      WHERE where id = 10 --or some parameter if you have a procedure
      ORDER BY REF -- add another column here if you decide to create one (e.g. date_created)

  OPEN mycur   
  FETCH NEXT FROM mycur INTO @Title, @Surname, @Forename, @DOB, @Add1, @Postcode 

  WHILE @@FETCH_STATUS = 0   
  BEGIN   
    SET @rTitle = isnull(@Title,@rTitle) -- update the result with the new value unless the new one is null
    ... -- repeat for all the variables
  END   

  CLOSE mycur
  DEALLOCATE mycur

--here use all the result variables for whatever you wish
END

orderbyref
在数据之后进行更新,因为基本上T>D

我添加了一个标识列id2以使逻辑工作

declare @t table(id2 int identity(1,1), 
REF char(1),
ID int,
Title varchar(10),
Surname varchar(10),
Forename varchar(10),
DOB date, 
Add1 varchar(15),
Postcode varchar(10)
)

insert @t values

('D',10, 'MR', 'KINGSTON', NULL, '19750715', '3 WATER SQUARE', NULL),
('T',10, NULL, NULL, 'BOB', NULL, NULL, NULL),
('T',10, 'MRS', NULL, NULL, NULL, NULL, 'TW13')

select Ref, t2.Title, t3.Surname, t4.Forename, t5.Dob, t6.Add1, t7.PostCode from @t t1
outer apply (select top 1 Title from @t where t1.id = id and Title is not null
order by id2 desc) t2
outer apply (select top 1 Surname from @t where t1.id = id and Surname is not null
order by id2 desc) t3
outer apply (select top 1 Forename from @t where t1.id = id and Forename is not null
order by id2 desc) t4
outer apply (select top 1 DOB from @t where t1.id = id and DOB is not null
order by id2 desc) t5
outer apply (select top 1 add1 from @t where t1.id = id and add1 is not null
order by id2 desc) t6
outer apply (select top 1 postcode from @t where t1.id = id and postcode is not null
order by id2 desc) t7
where Ref = 'D'
结果:

Ref Title  Surname  Forename  Dob         Add1            PostCode
D   MRS    KINGSTON BOB       1975-07-15  3 WATER SQUARE  TW13

合并时,您会给予什么优先权?在标题中,您选择了MR的MRS insead。@DinupKandel
T=Update
如何处理同一列上的多个更新?我将优先考虑MRS,因为它是第二个事务,因此将覆盖MR。您如何确定哪一个事务是第二个事务?请不要告诉我这是因为它是最下面的一个,您需要一个类似timestamp或id的列来“orderby”来描述它是哪一个is@Nitz别忘了接受答案。请注意,如果一个列的值更改不止一次,那么这并不总是有效。如果我在主表上有一个标识列,那么CTE会是什么样子?@Nitz:我已经编辑了答案以包含该解决方案。在开始重写CTE之后,最终它变成了几乎完全不同的解决方案。不再需要加入D和T,因为我想T总是会有比D更新的IDNT。有没有一种方法可以在不输入它们的情况下对所有字段都这样做?像SELECT*UNION ALL SELECT r.ID合并*
declare @t table(id2 int identity(1,1), 
REF char(1),
ID int,
Title varchar(10),
Surname varchar(10),
Forename varchar(10),
DOB date, 
Add1 varchar(15),
Postcode varchar(10)
)

insert @t values

('D',10, 'MR', 'KINGSTON', NULL, '19750715', '3 WATER SQUARE', NULL),
('T',10, NULL, NULL, 'BOB', NULL, NULL, NULL),
('T',10, 'MRS', NULL, NULL, NULL, NULL, 'TW13')

select Ref, t2.Title, t3.Surname, t4.Forename, t5.Dob, t6.Add1, t7.PostCode from @t t1
outer apply (select top 1 Title from @t where t1.id = id and Title is not null
order by id2 desc) t2
outer apply (select top 1 Surname from @t where t1.id = id and Surname is not null
order by id2 desc) t3
outer apply (select top 1 Forename from @t where t1.id = id and Forename is not null
order by id2 desc) t4
outer apply (select top 1 DOB from @t where t1.id = id and DOB is not null
order by id2 desc) t5
outer apply (select top 1 add1 from @t where t1.id = id and add1 is not null
order by id2 desc) t6
outer apply (select top 1 postcode from @t where t1.id = id and postcode is not null
order by id2 desc) t7
where Ref = 'D'
Ref Title  Surname  Forename  Dob         Add1            PostCode
D   MRS    KINGSTON BOB       1975-07-15  3 WATER SQUARE  TW13