Selecting multiple columns to one string (by date) - sql

My pricelist table looks like this:
ItemCode VendorCode UnitCost StartingDate
333 362 2.31 2016-08-19 00:00:00.0
333 362 2.16 2018-02-22 00:00:00.0
444 362 12.96 2014-01-09 00:00:00.0
444 362 13.10 2015-01-09 00:00:00.0
444 430 13.05 2017-04-01 00:00:00.0
444 550 13.30 2018-02-01 00:00:00.0
I would like to have query result following:
333:(362,2.16,2018-02-22)
444:(362,13.10,2015-01-09),(430,13.05,2017-04-01),(550,13.30,2018-02-01)
So all different Vendors and their prices should be listed and only latest by date.
I got this far:
SELECT
Pricelist.ItemCode + ': '+ temp.data
FROM Pricelist
INNER JOIN (SELECT p1.ItemCode,
STUFF((SELECT '; ' + p2.VendorCode
FROM Pricelist p2
WHERE p2.ItemCode = p1.ItemCode
ORDER BY VendorCode
FOR XML PATH('')), 1, 1, '') AS data
FROM Pricelist p1) as temp
ON Pricelist.ItemCode = temp.ItemCode
GROUP BY Pricelist.ItemCode, temp.data
ORDER BY 1
But not even close to the result I need.

I would use row_number() function :
select concat(itemcode, ':',
stuff( ( select top (1) with ties ',(' +concat(VendorCode, ',', UnitCost ,',', cast(StartingDate as date)) +')'
from Pricelist
where itemcode = p.itemcode
order by row_number() over (partition by VendorCode order by StartingDate desc)
for xml path('')
), 1, 1, ''
))
from Pricelist p
group by itemcode;

Try with this query:
create table #t (ItemCode int, VendorCode int, UnitCost decimal (10,2), StartingDate datetime)
insert into #t values
(333,362, 2.31,'2016-08-19 00:00:00.0'),
(333,362, 2.16,'2018-02-22 00:00:00.0'),
(444,362,12.96,'2014-01-09 00:00:00.0'),
(444,362,13.10,'2015-01-09 00:00:00.0'),
(444,430,13.05,'2017-04-01 00:00:00.0'),
(444,550,13.30,'2018-02-01 00:00:00.0')
;with tr1 as (
select
convert(varchar(100),ItemCode) + ':' as ItemCode,
'(' + convert(varchar(100),VendorCode) + ',' + convert(varchar(100),UnitCost) + ',' + convert(varchar(19),StartingDate,121) + ')' as Vals,
row_number() over (partition by ItemCode,VendorCode order by StartingDate desc) rn
from #t
)
select distinct ItemCode,
stuff((
select ',' + Vals
from tr1 b
where b.ItemCode=tr1.ItemCode
and rn=1
for xml path ('')
),1,1,'')
from tr1
where rn=1

First grouping the itemcodes, and then linking that to a string with the vendor details might be quite performant.
Linking those itemcodes to an OUTER APPLY with a FOR XML works well.
For example :
declare #Pricelist table (ItemCode int, VendorCode int, UnitCost decimal (10,2), StartingDate datetime)
insert into #Pricelist values
(333,362,02.31,'2016-08-19T00:01:00'),
(333,362,02.16,'2018-02-22T00:02:00'),
(444,362,12.96,'2014-01-09T00:03:00'),
(444,362,13.10,'2015-01-09T00:04:00'),
(444,430,13.05,'2017-04-01T00:05:00'),
(444,550,13.30,'2018-02-01T00:06:00');
select concat(itemcode,':',stuff(x.details,1,1,'')) as ItemVendorDetails
from (select distinct itemcode from #Pricelist) i
outer apply
(
select top 1 with ties
concat(',(',VendorCode,',',UnitCost,',',convert(date,StartingDate),')')
from #Pricelist p
where p.ItemCode = i.ItemCode
order by row_number() over (partition by ItemCode, VendorCode order by StartingDate desc)
for xml path('')
) x(details);
Result:
ItemVendorDetails
------------------------------------------------------------------------
333:(362,2.16,2018-02-22)
444:(362,13.10,2015-01-09),(430,13.05,2017-04-01),(550,13.30,2018-02-01)

The following query works with the sample data provided:
;WITH VendorPerItemCTE AS (
SELECT ItemCode,
VendorCode,
UnitCost,
StartingDate,
ROW_NUMBER() OVER (PARTITION BY ItemCode, VendorCode
ORDER BY StartingDate DESC) AS seq
FROM PriceList
)
SELECT CAST(ItemCode AS VARCHAR(12)) + ':' + STUFF(ItemData , 1, 1, '')
FROM (
SELECT DISTINCT p.ItemCode,
(SELECT ', (' + CAST(VendorCode AS VARCHAR(10)) + ', ' +
CAST(UnitCost AS VARCHAR(10)) + ', ' +
CONVERT(VARCHAR(12), StartingDate, 102 ) +
')'
FROM VendorPerItemCTE AS c
WHERE p.ItemCode = c.ItemCode AND c.seq = 1
FOR XML PATH('')) AS ItemData
FROM PriceList AS p) AS t
Demo here

Related

Multiple Rows into One Row multiple columns

We need to list all numbers as a flat data set, how can we do that?
Table Name: Telephone
ID TYPE NUMBER
==================================
123 MN 042153939
123 HN 2242116
123 MN 1234567890
123 HN 12345678
Create Table Telephone
(
ID Integer,
Type char(3),
Number Varchar(20)
);
insert into Telephone values
(123, 'MN', '042153939'),
(123, 'HN', '2242116'),
(123, 'MN', '1234567890'),
(123, 'HN', '12345678');
I want SQL to return data in this format
ID MN#1 Mn#2 HN#1 HN#2
================================================
123 042153939 1234567890 2242116 12345678
Dynamic approach
Init
DROP TABLE IF EXISTS #Telephone;
CREATE TABLE #Telephone(ID INT,Type CHAR(3),Number VARCHAR(20));
INSERT INTO #Telephone (ID,Type,Number) VALUES
(123, 'MN', '042153939'),
(123, 'HN', '2242116'),
(123, 'MN', '1234567890'),
(123, 'HN', '12345678');
The code
DECLARE #ColumnList NVARCHAR(MAX);
SELECT #ColumnList = STUFF((SELECT ',[' + RTRIM(t.[Type]) + '#'
+ CONVERT(NVARCHAR(255),ROW_NUMBER()OVER(PARTITION BY t.[Type] ORDER BY t.ID)) + ']'
FROM #Telephone t FOR XML PATH(''),TYPE).value('(./text())[1]','NVARCHAR(MAX)'),1,1,'')
;
DECLARE #sql NVARCHAR(MAX) = '';
SET #sql = N'
SELECT ID,' + #ColumnList + N'
FROM (
SELECT t.ID,t.Number, RTRIM(t.[Type]) + ''#'' + CONVERT(NVARCHAR(255),ROW_NUMBER()OVER(PARTITION BY t.[Type] ORDER BY t.ID)) AS [Type]
FROM #Telephone t
) a
PIVOT(MAX(a.Number) FOR a.Type IN (' + #ColumnList + N')) p
'
;
--PRINT #sql
IF #sql IS NOT NULL EXEC(#sql);
try pivoting like below :
SELECT first_column AS <first_column_alias>,
[pivot_value1], [pivot_value2], ... [pivot_value_n]
FROM
(<source_table>) AS <source_table_alias>
PIVOT
(
aggregate_function(<aggregate_column>)
FOR <pivot_column> IN ([pivot_value1], [pivot_value2], ... [pivot_value_n])
) AS <pivot_table_alias>;
We can try using a pivot query with the help of ROW_NUMBER():
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY TYPE DESC, NUMBER) rn
FROM Telephone
)
SELECT
ID,
MAX(CASE WHEN rn = 1 THEN NUMBER END) AS [MN#1],
MAX(CASE WHEN rn = 2 THEN NUMBER END) AS [MN#2],
MAX(CASE WHEN rn = 3 THEN NUMBER END) AS [HN#3],
MAX(CASE WHEN rn = 4 THEN NUMBER END) AS [HN#4]
FROM cte
GROUP BY ID;
You may try this. with row_number() and pivot.
For more info about pivot you may find this link PIVOT.
; with cte as (
select row_number() over (partition by type order by id ) as Slno, * from Telephone
)
, ct as (
select id, type + '#' + cast(slno as varchar(5)) as Type, values from cte
)
select * from (
select * from ct
) as d
pivot
( max(values) for type in ( [MN#1],[Mn#2],[HN#1],[HN#2] )
) as p

Combine Multi Rows with COALESCE

Using SQL-Server 2012
I have the following Table:
Id Description
6192 Salzburg
6193 Salzburg
6194 Salzburg
6196 Innsbruck
6197 Innsbruck
6198 Innsbruck
6199 Innsbruck
6201 Bregenz
6202 Bregenz
6203 Bregenz
I want to Select each Distinct "Description" with all the Id's together in one string:
Description Ids
Salzburg '6192,6193,6194'
Innsbruck '6196,6197,6198'
I saw some similar code on this site [How to concatenate text from multiple rows into a single text string in SQL server?, but I couldn't figure it out yet for my purpose (don't want to use XML Path!). Here is what I have tried so far:
DECLARE #ids AS Nvarchar(MAX)
SELECT #ids = COALESCE(#ids + ',', '') + CAST(t.Id AS nvarchar(5))
FROM (SELECT tmp.Id FROM (SELECT id, [Description] FROM tblMasterPropValues WHERE IdCategory = 253 AND IsActive = 1) as tmp
WHERE [Description] = tmp.[Description]) AS t
SELECT #ids
--SELECT DISTINCT [Description], #ids AS IDs FROM tblMasterPropValues WHERE IdCategory = 253 AND IsActive = 1 AND Id IN (#ids)
I can't really get my head around it, and would appreciate any help on it.
You can try using STUFF() function
SELECT description, Ids = STUFF(
(SELECT ',' + Id
FROM tblMasterPropValues t1
WHERE t1.description = t2.description
FOR XML PATH (''))
, 1, 1, '') from tblMasterPropValues t2
group by description;
For that FOR XML PATH() is the right clause so, you can do :
SELECT DISTINCT v.description, STUFF(v1.ids, 1, 1, '''') + ''''
FROM tblMasterPropValues v CROSS APPLY
(SELECT ', '+ CAST(v1.Id AS VARCHAR(255))
FROM tblMasterPropValues v1
WHERE v1.description = v.description
FOR XML PATH('')
) v1(ids);
You can also make it by using recursive CTE
DECLARE #tblMasterPropValues TABLE (Id INT, Description VARCHAR(20))
INSERT INTO #tblMasterPropValues VALUES
(6192 , 'Salzburg'),
(6193 , 'Salzburg'),
(6194 , 'Salzburg'),
(6196 , 'Innsbruck'),
(6197 , 'Innsbruck'),
(6198 , 'Innsbruck'),
(6199 , 'Innsbruck'),
(6201 , 'Bregenz'),
(6202 , 'Bregenz'),
(6203 , 'Bregenz')
;WITH Tbl AS
(
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY Description ORDER BY Id) AS RN,
COUNT(*) OVER(PARTITION BY Description) AS CNT
FROM #tblMasterPropValues
)
, Rcr AS (
SELECT *, CAST(Id AS varchar(max)) Ids
FROM Tbl WHERE RN = 1
UNION ALL
SELECT T.*, Rcr.Ids + ',' + CAST(T.Id AS VARCHAR(10)) Ids
FROM Rcr
INNER JOIN Tbl T ON T.RN = Rcr.RN + 1 and Rcr.Description = T.Description
)
SELECT RN, Description, Ids FROM Rcr
WHERE RN = CNT
Result:
Description Ids
-------------------- -----------------------
Salzburg 6192,6193,6194
Innsbruck 6196,6197,6198,6199
Bregenz 6201,6202,6203
Try this:
DECLARE #Table TABLE(ID INT, Description VARCHAR(25))
INSERT INTO #Table
VALUES (6192,'Salzburg' )
,(6193,'Salzburg' )
,(6194,'Salzburg' )
,(6196,'Innsbruck')
,(6197,'Innsbruck')
,(6198,'Innsbruck')
,(6199,'Innsbruck')
,(6201,'Bregenz' )
,(6202,'Bregenz' )
,(6203,'Bregenz' )
Query:
SELECT DISTINCT T2.Description,
SUBSTRING(
(
SELECT ','+CAST(T1.ID AS VARCHAR) AS [text()]
FROM #Table T1
WHERE T1.Description = T2.Description
ORDER BY T1.Description
FOR XML PATH ('')
), 2, 1000) [Ids]
FROM #Table T2
Result:
Description Ids
Bregenz 6201,6202,6203
Innsbruck 6196,6197,6198,6199
Salzburg 6192,6193,6194

Putting comma separated values into separate or different columns

I have a table and values as shown here
create table #example(id int primary key identity, cols varchar(255))
insert into #example(cols) values('HI,HELLO,BYE,TC')
insert into #example(cols) values('WHAT,ARE,YOU,DOING,HERE')
I need the resultant output as shown in the picture
Note : There is no limit for values i.e dynamic
This is what you are expecting
Schema:
CREATE TABLE #EXAMPLE(ID INT PRIMARY KEY IDENTITY, COLS VARCHAR(255))
INSERT INTO #EXAMPLE(COLS) VALUES('HI,HELLO,BYE,TC')
INSERT INTO #EXAMPLE(COLS) VALUES('WHAT,ARE,YOU,DOING,HERE')
Do split those comma separated values into Rows and Apply pivot
SELECT * FROM (
SELECT id
, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT 1)) AS ID2
, SPLT.CLMS.value('.','VARCHAR(MAX)') AS LIST FROM (
select id
, CAST( '<M>'+REPLACE(cols,',','</M><M>')+'</M>' AS XML) AS XML_COL from #example E
)E
CROSS APPLY E.XML_COL.nodes('/M') AS SPLT(CLMS)
)A
PIVOT
(
MAX(LIST) FOR ID2 IN ([1],[2],[3],[4],[5])
)PV
You will get result as
+----+------+-------+-----+-------+------+
| id | 1 | 2 | 3 | 4 | 5 |
+----+------+-------+-----+-------+------+
| 1 | HI | HELLO | BYE | TC | NULL |
| 2 | WHAT | ARE | YOU | DOING | HERE |
+----+------+-------+-----+-------+------+
Edit:
And now you need to go for dynamic pivot,since There is no limit for values.
DECLARE #COLS VARCHAR(MAX)='', #QRY VARCHAR(MAX)='';
SELECT #COLS =#COLS+'['+CAST( ID2 AS VARCHAR(10))+'],' FROM (
SELECT DISTINCT ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT 1)) AS ID2 FROM (
select id, CAST( '<M>'+REPLACE(cols,',','</M><M>')+'</M>' AS XML) AS XML_COL from #example E
)E
CROSS APPLY E.XML_COL.nodes('/M') AS SPLT(CLMS)
)A
SELECT #COLS = LEFT(#COLS,LEN(#COLS)-1)
SELECT #QRY =
'
SELECT * FROM (
SELECT id
, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT 1)) AS ID2
, SPLT.CLMS.value(''.'',''VARCHAR(MAX)'') AS LIST FROM (
select id, CAST( ''<M>''+REPLACE(cols,'','',''</M><M>'')+''</M>'' AS XML) AS XML_COL from #example E
)E
CROSS APPLY E.XML_COL.nodes(''/M'') AS SPLT(CLMS)
)A
PIVOT
(
MAX(LIST) FOR ID2 IN ('+#COLS+ ')
)PV'
EXEC( #QRY)
Dynamic version base on split string then pivot table
Schema:
create table #example(id int primary key identity, cols varchar(255))
insert into #example(cols) values('HI,HELLO,BYE,TC')
insert into #example(cols) values('WHAT,ARE,YOU,DOING,HERE')
Calculate columns pivot
-- Calculate dynamic columns
;WITH temps AS
(
SELECT sd.* ,CAST('<x>' + replace(sd.cols, ',', '</x><x>') + '</x>' as xml) AS xmlText
FROM #example sd
),
temps1 AS
(
SELECT t.Id, t.cols, v.x.value('.','varchar(50)') AS Value
FROM temps t
CROSS APPLY
t.xmlText.nodes('/x') AS v(x)
)
SELECT #ColumnPivot = STUFF((SELECT DISTINCT CONCAT(',COL',row_number() OVER(PARTITION BY t.id ORDER BY t.Value)) FROM temps1 t FOR XML PATH('')), 1,1,'')
PRINT #ColumnPivot
Then PIVOT table
DECLARE #query nvarchar(max) =
CONCAT(N';WITH temps AS
(
SELECT sd.* ,CAST(''<x>'' + replace(sd.cols, '','', ''</x><x>'') + ''</x>'' as xml) AS xmlText
FROM #example sd
),
temps1 AS
(
SELECT t.Id, t.cols, v.x.value(''.'' , ''varchar(50)'') AS Value
FROM temps t
CROSS APPLY
t.xmlText.nodes(''/x'') AS v(x)
),
temps2 AS
(
SELECT t.* , CONCAT(''COL'',row_number() OVER(PARTITION BY t.id ORDER BY (select 1))) AS ColGroup
FROM temps1 t
)',
N'SELECT Id, cols, ' , #ColumnPiVot,
' FROM
(
select * from temps2
) AS src
PIVOT
(MAX(Value) FOR ColGroup IN (',#ColumnPiVot,')) AS pvt')
PRINT #query
exec sp_executesql #query
DROP TABLE #example
Demo link: Rextester
DECLARE #COLS AS NVARCHAR(MAX),
#QUERY AS NVARCHAR(MAX);
SET #COLS = STUFF((SELECT DISTINCT ',' + QUOTENAME(C.RN)
FROM (SELECT ID,TOKEN,COLS
,ROW_NUMBER() OVER (
PARTITION BY ID ORDER BY (
SELECT NULL
)
) AS RN
FROM #EXAMPLE
CROSS APPLY (
SELECT *
FROM UDF_SPLITSTRING(COLS, ',')
) A) C
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #QUERY = 'SELECT ID,cols, ' + #COLS + ' FROM
(
SELECT ID,TOKEN,cols
,ROW_NUMBER() OVER (
PARTITION BY ID ORDER BY (
SELECT NULL
)
) AS RN
FROM #EXAMPLE
CROSS APPLY (
SELECT *
FROM UDF_SPLITSTRING(COLS, '','')
) A
) X
PIVOT
(
MAX(TOKEN)
FOR rn IN (' + #COLS + ')
) P '
exec(#QUERY)
output
ID cols 1 2 3 4 5
1 HI,HELLO,BYE,TC HI HELLO BYE TC NULL
2 WHAT,ARE,YOU,DOING,HERE WHAT ARE YOU DOING HERE

How to split data of 1 column in 3 columns?

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?
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..
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
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);
ONLINE DEMO

Concatenating multiple fields across multiple rows

Assuming I have a query returning
OrderID Date Product Count
1 2015-01-01 "Bread" 1
2 2015-02-02 "Water" 2
2 2015-02-02 "Bread" 7
2 2015-02-02 "Soap" 3
How can I make this query to return 2 records (group by ID) concatenating multiple columns
OrderID Date Detail
1 2015-01-01 "Bread (1)"
2 2015-02-02 "Bread (7), Soap (3), Water (2)"
Where I concatenate:
Product Names and their Count value showing (in brackets)
Order by Product Name
into a "Detail" NVarchar result ??
you didn't mention if date is different for same orderid then what will happen.
Try this,
Declare #orders TABLE ([OrderID] INT,[Date] DATE,[Product] VARCHAR(10),[Count] INT)
INSERT INTO #orders
VALUES (1,'2015-01-01','Bread',1),
(2,'2015-02-02','Water',2),
(2,'2015-02-02','Bread',7),
(2,'2015-02-02','Soap',3),
(2,'2015-03-02','Soap',3)
;WITH CTE
AS (
SELECT *
,ROW_NUMBER() OVER (
PARTITION BY orderid
,[date] ORDER BY [date]
) rn
FROM #orders
)
SELECT OrderID
,[date]
,stuff((
SELECT ',' + Product + ' ( ' + CONVERT(VARCHAR,[Count]) + ' ) '
FROM cte
WHERE orderid = a.OrderID
AND [Date] = a.[Date]
FOR XML path('')
), 1, 1, '') [details]
FROM CTE A
WHERE rn = 1
I'll let you handle the trailing comma removal.
CREATE TABLE orders ([OrderID] INT,[Date] DATE,[Product] VARCHAR(10),[Count] INT)
INSERT INTO orders
VALUES (1,'2015-01-01','Bread',1),
(2,'2015-02-02','Water',2),
(2,'2015-02-02','Bread',7),
(2,'2015-02-02','Soap',3)
SELECT
OrderID,
[Date],
(
SELECT DISTINCT
[Product] + '(' + CONVERT(VARCHAR,[Count]) + '), '
FROM
orders o1
WHERE o1.OrderID = o.OrderID
FOR XML PATH('')
) Detail
FROM orders o
GROUP BY OrderID, [Date]