Reputation: 39
Data is in given format-
Id Date Location
a123 6/6/2016 mmp
a123 6/7/2016 jpr
a123 6/8/2016 hjl
a123 6/9/2016 jhag
a678 6/10/2016 hjlwe
a678 6/11/2016 mkass
a980 6/7/2016 asdadf
a980 6/7/2016 lasdj
a980 6/7/2016 xswd
I want the same in given format-:
Id Date 1 Location1 Date 2 Location 2 Date 3 Location 3
a123 6/6/2016 mmp 6/7/2016 jpr 6/8/2016 hjl
a678 6/10/2016 hjlwe 6/11/2016 mkass
a980 6/7/2016 asdadf 6/7/2016 lasdj 6/7/2016
How to do that in SQL?
Upvotes: 2
Views: 114
Reputation: 15977
Also you can make it with PIVOT
(if number of columns can change dynamically you must use dynamic SQL):
;WITH cte AS (
SELECT *
FROM (VALUES
('a123', '6/6/2016', 'mmp'),
('a123', '6/7/2016', 'jpr'),
('a123', '6/8/2016', 'hjl'),
('a123', '6/9/2016', 'jhag'),
('a678', '6/10/2016', 'hjlwe'),
('a678', '6/11/2016', 'mkass'),
('a980', '6/7/2016', 'asdadf'),
('a980', '6/7/2016', 'lasdj'),
('a980', '6/7/2016', 'xswd')
) as t(Id, [Date], [Location])
)
SELECT p1.Id,
p1.[Date1],
p2.[Location1],
p1.[Date2],
p2.[Location2],
p1.[Date3],
p2.[Location3],
p1.[Date4],
p2.[Location4]
FROM
(SELECT *
FROM (
SELECT Id,
[Date],
'Date' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Date]) AS NVARCHAR(10)) as rn
fROM cte
) AS D
PIVOT (
MAX([Date]) for RN in ([Date1],[Date2],[Date3],[Date4])
) as pvt
) as p1
LEFT JOIN
(SELECT *
FROM (
SELECT Id,
[Location],
'Location' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Date]) AS NVARCHAR(10)) as rn
fROM cte
) AS D
PIVOT (
MAX([Location]) for RN in ([Location1],[Location2],[Location3],[Location4])
) as pvt
) as p2
ON p1.Id = p2.Id
Output:
Id Date1 Location1 Date2 Location2 Date3 Location3 Date4 Location4
---- --------- --------- --------- --------- --------- --------- --------- ---------
a123 6/6/2016 mmp 6/7/2016 jpr 6/8/2016 hjl 6/9/2016 jhag
a678 6/10/2016 hjlwe 6/11/2016 mkass NULL NULL NULL NULL
a980 6/7/2016 lasdj 6/7/2016 xswd 6/7/2016 asdadf NULL NULL
EDIT
With dynamic SQL (same output):
CREATE TABLE #temp (
Id nvarchar(10),
[Date] date,
[Location] nvarchar(10)
)
INSERT INTO #temp VALUES
('a123', '6/6/2016', 'mmp'),
('a123', '6/7/2016', 'jpr'),
('a123', '6/8/2016', 'hjl'),
('a123', '6/9/2016', 'jhag'),
('a678', '6/10/2016', 'hjlwe'),
('a678', '6/11/2016', 'mkass'),
('a980', '6/7/2016', 'asdadf'),
('a980', '6/7/2016', 'lasdj'),
('a980', '6/7/2016', 'xswd')
DECLARE @locs nvarchar(max),
@dates nvarchar(max),
@cols nvarchar(max),
@sql nvarchar(max)
SELECT @locs = STUFF((
SELECT DISTINCT ',' + QUOTENAME('Location' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Date]) AS NVARCHAR(10)))
FROM #temp
FOR XML PATH('')
),1,1,'')
SELECT @dates = STUFF((
SELECT DISTINCT ',' + QUOTENAME('Date' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Date]) AS NVARCHAR(10)))
FROM #temp
FOR XML PATH('')
),1,1,'')
SELECT @cols = STUFF((
SELECT DISTINCT ',' + QUOTENAME('Date' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Date]) AS NVARCHAR(10))) +
',' + QUOTENAME('Location' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Date]) AS NVARCHAR(10)))
FROM #temp
FOR XML PATH('')
),1,1,'')
SELECT @sql ='
SELECT p1.Id,
'+@cols+'
FROM
(SELECT *
FROM (
SELECT Id,
[Date],
''Date'' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Date]) AS NVARCHAR(10)) as rn
fROM #temp
) AS D
PIVOT (
MAX([Date]) for RN in ('+@dates+')
) as pvt
) as p1
LEFT JOIN
(SELECT *
FROM (
SELECT Id,
[Location],
''Location'' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Date]) AS NVARCHAR(10)) as rn
fROM #temp
) AS D
PIVOT (
MAX([Location]) for RN in ('+@locs+')
) as pvt
) as p2
ON p1.Id = p2.Id'
EXECUTE sp_executesql @sql
DROP TABLE #temp
Upvotes: 2
Reputation: 31879
If you don't know how many columns you have, you can use a dynamic version of sagi's answer. Here is one using a dynamic crosstab:
DECLARE @sql NVARCHAR(MAX) = N'';
SELECT @sql =
'SELECT
t.id' + CHAR(10);
SELECT @sql = @sql +
(SELECT
' , MAX(CASE WHEN rn = ' + CAST(rn AS VARCHAR(10)) + ' THEN t.Date END) AS ' + QUOTENAME('Date' + CAST(rn AS VARCHAR(10))) + CHAR(10) +
' , MAX(CASE WHEN rn = ' + CAST(rn AS VARCHAR(10)) + ' THEN t.Location END) AS ' + QUOTENAME('Location' + CAST(rn AS VARCHAR(10))) + CHAR(10)
FROM (
SELECT DISTINCT
ROW_NUMBER() OVER (PARTITION BY Id ORDER BY(SELECT NULL)) AS rn
FROM tbl
) t
ORDER BY rn
FOR XML PATH(''));
SELECT @sql = @sql +
'FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Id ORDER BY Date) AS rn
FROM tbl
) t
GROUP BY t.Id;';
PRINT (@sql);
EXEC (@sql);
Upvotes: 0
Reputation: 40471
You can use ROW_NUMBER()
with conditional aggregation :
SELECT s.id,
MAX(CASE WHEN s.rnk = 1 THEN s.date END) as date_1,
MAX(CASE WHEN s.rnk = 1 THEN s.location END) as location_1,
MAX(CASE WHEN s.rnk = 2 THEN s.date END) as date_2,
MAX(CASE WHEN s.rnk = 2 THEN s.location END) as location_2,
MAX(CASE WHEN s.rnk = 3 THEN s.date END) as date_3,
MAX(CASE WHEN s.rnk = 3 THEN s.location END) as location_3
FROM(
SELECT t.*,
ROW_NUMBER() OVER(PARTITION BY t.id ORDER BY t.Date) as rnk
FROM YourTable t) s
GROUP BY s.id
This can also be solved with PIVOT
but I prefer to use conditional aggregation as long as the amount of the columns is limited .
If you need to add more levels, just follow the logic and replace 3
with 4
and so on..
Upvotes: 2