SQL Server: объединение нескольких строк данных в одну строку

85
9

То, что я хочу сделать, это объединить несколько строк данных, которые будут отображаться как одна строка из Transact-SQL или SSIS. например,


MAKE:


REF  ID   Title Surname     Forename    DOB          Add1            Postcode
------------------------------------------------------------------------------------------
D 10 MR KINGSTON NULL 15/07/1975 3 WATER SQUARE NULL
T 10 NULL NULL BOB NULL NULL NULL
T 10 MRS NULL NULL NULL NULL TW13 7DT

в это:


REF  ID   Title Surname    Forename   DOB          Add1            Postcode
----------------------------------------------------------------------------------
D 10 MRS KINGSTON BOB 15/07/1975 3 WATER SQUARE TW13 7DT

Итак, то, что я сделал, объединяет значение вместе, игнорируя значения, которые являются нулевыми. (D = данные, T = обновление)


Любые предложения были бы очень желанными.


Спасибо.

спросил(а) 2021-01-19T18:28:17+03:00 1 месяц, 1 неделя назад
1
Решение
73

Это будет работать, но поскольку не существует столбца identity или datetime, невозможно найти, какая строка обновления является более новой. Поэтому, если в одном столбце больше обновлений, я просто беру первый алфавит/число (MIN).


WITH CTE AS 
(
SELECT ID, REF, MIN(Title) Title, MIN(Surname) Surname, MIN(Forename) Forename, MIN(DOB) DOB, MIN(Add1) Add1, MIN(Postcode) Postcode
FROM Table1
GROUP BY id, REF
)
SELECT
d.REF
, d.ID
, COALESCE(T.Title, d.TItle) AS Title
, COALESCE(T.Surname, d.Surname) AS Surname
, COALESCE(T.Forename, d.Forename) AS Forename
, COALESCE(T.DOB, d.DOB) AS DOB
, COALESCE(T.Add1, d.Add1) AS Add1
, COALESCE(T.Postcode, d.Postcode) AS Postcode
FROM CTE d
INNER JOIN CTE t ON d.ID = t.ID AND d.REF = 'D' AND t.REF = 't'

SQLFiddle DEMO


Если столбец идентификатора можно добавить, мы можем просто переписать часть CTE, чтобы сделать ее более точной.

EDIT:


Если у нас есть столбец идентификаторов, а 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

SQLFiddle DEMO

ответил(а) 2021-01-19T18:28:17+03:00 1 месяц, 1 неделя назад
74

Я добавил идентификационный столбец 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

ответил(а) 2021-01-19T18:28:17+03:00 1 месяц, 1 неделя назад
43

Вы можете попробовать сделать это с помощью курсора:


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


ORDER BY REF делает обновления после данных, так как в основном T > D

ответил(а) 2021-01-19T18:28:17+03:00 1 месяц, 1 неделя назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема