SQL FOR XML PATH list and COUNT - sql-server

I have a table such as:
|Date |Name|
--------------------
|'20-May-2011'|Bob |
|'20-May-2011'|Fred|
|'20-May-2011'|Jim |
|'21-May-2011'|Bob |
|'21-May-2011'|Ed |
|'22-May-2011'|Bill|
I need a query to return:
|Date |Count|Names |
--------------------------------------
|'20-May-2011'| 3|'Bob, Fred, Jim'|
|'21-May-2011'| 2|'Bob, Ed' |
|'22-May-2011'| 1|'Bill' |
In other words, I want a list and a count of the names by date.
The best I can come up with is:
SELECT list.[Date], [Count], [Names]
FROM (
SELECT [Date],
STUFF((
SELECT ', ' + [Name]
FROM #table t2
WHERE t2.[Date] = t.[Date]
ORDER BY [Name]
FOR XML PATH('')
), 1, 2, '') AS [Names]
FROM #table t
GROUP BY [Date]
) [list]
INNER JOIN (
SELECT [Date],
COUNT(*) AS [Count]
FROM #table t
GROUP BY [Date]
) [count]
ON list.[Date] = count.[Date]
ORDER BY [Count] DESC, list.[Date]
Is there a more elegant query?

SELECT [Date],
COUNT(*) AS [Count],
STUFF((
SELECT ', ' + [Name]
FROM #table t2
WHERE t2.[Date] = t.[Date]
ORDER BY [Name]
FOR XML PATH('')
), 1, 2, '') AS [Names]
FROM #table t
GROUP BY [Date]
If you think that the Name column might contain <>'"& you should do like this instead:
SELECT [Date],
COUNT(*) AS [Count],
STUFF((
SELECT ', ' + [Name]
FROM #table t2
WHERE t2.[Date] = t.[Date]
ORDER BY [Name]
FOR XML PATH(''), TYPE
).value('.', 'varchar(max)'), 1, 2, '') AS [Names]
FROM #table t
GROUP BY [Date]

Not a whole lot better - but maybe using a single CTE to "encapsulate" the XML-PATH-stuffing into a more presentable way would work??
;WITH ConsolidatedData AS
(
SELECT
[Date],
STUFF((
SELECT ', ' + [Name]
FROM #table t2
WHERE t2.[Date] = t.[Date]
ORDER BY [Name]
FOR XML PATH('')
), 1, 2, '') AS [Names]
FROM #table t
)
SELECT
[Date], Names, COUNT(*)
FROM
ConsolidatedData
GROUP BY
[Date], Names
Not sure if you'd count this as one "compound" statement, or two.... :-)
One word of advice: try not to use SQL Server identifiers and reserved words (like Date or Order) as your own column and/or table names.... it's always rather messy....

Related

How to group all values by semicolon separated in SQL query

Please help how to achieve data as per shown in attached image
Highlighted rows actually group on Item Number field
Thanks in advance
Following should work:
SELECT distinct ItemNumber,SUBSTRING(
(SELECT ';' + TotalItem+' x '+ItemName
FROM YourTable
where ItemNumber=c.ItemNumber
ORDER BY ItemNumber
FOR XML PATH('')),2,200000 ) AS NewDescription from YourTable c
GO
Here's one way:
SELECT
it.[Item Number]
, Values1 = STUFF(CAST((
SELECT [text()] = ', ' + CAST(it2.TotalItem AS varchar) + ' x ' + it2.ItemName
FROM dbo.Items it2
WHERE it2.[Item Number] = it.[Item Number]
FOR XML PATH(''), TYPE) AS VARCHAR(100)), 1, 2, '')
FROM (
SELECT DISTINCT [Item Number]
FROM dbo.Items
) it
You could use STRING_AGG
SELECT [Item Number], STRING_AGG (CONCAT(TotalItem,'x',ItemName), ';') as tags
FROM dbo.TableName
GROUP BY [Item Number];
SELECT t2.ItemNumber ,
STUFF(( SELECT ',' + CAST(TotalItem AS VARCHAR) + ' x ' + ItemName
FROM #MyTable t1
WHERE t1.ItemNumber = t2.ItemNumber
FOR
XML PATH('')
), 1, 1, '') AS NewDescription
FROM #MyTable t2
GROUP BY t2.ItemNumber
DEMO

How to concatenate multiple rows into one based on id in sql server

Actually i have three columns. ID, MedID, Name. With my query i am getting a result look like the below.
ID MedID Name
1 101 TestData1
1 124 TestData2
Query
select
cmd.ID, mm.MedID,mm.Name as Name
from
tbl1 mm
inner join
tbl2 cmd on cmd.medID= mm.MedID
where
cmd.ID= 1
Actually i am expecting a result like the below
ID MedID Name
1 101,124 TestData1,TestData2
So, how can get a result like this, Where i need to do changes in my query?
Any help appreciated. Thanks in advance !!!
Use STUFF and FOR XML PATH like this:
select id,
stuff((
select ',' + cast(MedId as varchar(30))
from t t1
where t1.id = t.id
order by MedID
for xml path('')
), 1, 1, '') MedID,
stuff((
select ',' + Name
from t t1
where t1.id = t.id
order by MedID
for xml path('')
), 1, 1, '') Name
from t
group by id;
Demo
DECLARE #TABLE TABLE (ID int, MedID varchar(10), Name varchar(10))
INSERT INTO #TABLE VALUES
(1, '101' , 'TestData1'),
(1, '124' , 'TestData2')
SELECT t.ID
, STUFF(( SELECT ', ' + MedID
FROM #TABLE
WHERE ID = t.ID
FOR XML PATH(''),TYPE)
.value('.','NVARCHAR(MAX)'),1,2,'') AS MedID
, STUFF(( SELECT ', ' + Name
FROM #TABLE
WHERE ID = t.ID
FOR XML PATH(''),TYPE)
.value('.','NVARCHAR(MAX)'),1,2,'') AS Name
FROM #TABLE t
GROUP BY t.ID

how shall i transpose rows into new columns only if duplicates appears in rows?

I have a table like this:
I need to get output like this:
I tried to use transposing, but I am not getting someone please give idea to do this.
Creation script for table:
CREATE TABLE #T1 (
itemid int,
pack int,
UOM nvarchar(2)
)
INSERT INTO #T1 VALUES
(1,1,'EA'),
(1,10,'BX'),
(1,100,'CA'),
(2,1,'EA'),
(2,10,'RL')
If you need simple, fixed column solution, you can use:
SELECT * INTO #temp FROM
(VALUES
(1, 1, 'EA'),
(1, 10, 'BX'),
(1, 100, 'CA'),
(2, 1, 'EA'),
(2, 10, 'RL')) T(ItemId, Pack, UOM)
SELECT ItemId, MAX([1]) Pack1, MAX([U1]) UOM1, MAX([2]) Pack1, MAX([U2]) UOM1, MAX([3]) Pack1, MAX([U3]) UOM1 FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY Pack) PackNum,
'U'+CONVERT(varchar(10), ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY Pack)) UOMNum
FROM #temp
) T
PIVOT
(
MAX(Pack) FOR PackNum IN ([1], [2], [3])
) PPack
PIVOT
(
MAX(UOM) FOR UOMNum IN ([U1],[U2],[U3])
) PUOM
GROUP BY ItemId
If number of columns is not fixed, you must do it dynamically (see #gofr1) or:
DECLARE #count TABLE(N varchar(10))
INSERT #count
SELECT CONVERT(varchar(10), ROW_NUMBER() OVER (ORDER BY (SELECT 1)))
FROM #temp WHERE ItemId =
(
SELECT TOP 1 ItemId
FROM #temp
GROUP BY ItemId
ORDER BY COUNT(*) DESC
)
DECLARE #select varchar(MAX) = STUFF((SELECT ', MAX(['+N+']) Pack'+N+', MAX([U'+N+']) UOM'+N FROM #count FOR XML PATH('')), 1, 2, '')
DECLARE #pivot1 varchar(MAX) = STUFF((SELECT ', ['+N+']' FROM #count FOR XML PATH('')), 1, 2, '')
DECLARE #pivot2 varchar(MAX) = STUFF((SELECT ', [U'+N+']' FROM #count FOR XML PATH('')), 1, 2, '')
DECLARE #sql varchar(MAX) = '
SELECT ItemId, '+#select+' FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY Pack) PackNum,
''U''+CONVERT(varchar(10), ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY Pack)) UOMNum
FROM #temp
) T
PIVOT
(
MAX(Pack) FOR PackNum IN ('+#pivot1+')
) PPack
PIVOT
(
MAX(UOM) FOR UOMNum IN ('+#pivot2+')
) PUOM
GROUP BY ItemId'
EXEC(#sql)
Dynamic SQL and some pivoting:
DECLARE #sql nvarchar(max),
#columns nvarchar(max)
SELECT #columns = (
SELECT DISTINCT ','+QUOTENAME('pack' + cast(ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY itemid) as nvarchar(max)))+','+
QUOTENAME('UOM'+ cast(ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY itemid) as nvarchar(max)))
FROM #T1
FOR XML PATH('')
)
SELECT #sql = N'
SELECT *
FROM (
SELECT itemid,
Items+rn as Items,
[Values]
FROM (
SELECT itemid,
CAST(UOM as nvarchar(max)) as UOM,
CAST(pack as nvarchar(max)) as pack,
CAST(ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY (SELECT NULL)) as nvarchar(max)) as rn
FROM #T1
) as t
UNPIVOT (
[Values] FOR Items in ([UOM],[pack])
) as up
) t1
PIVOT (
MAX([Values]) FOR Items IN ('+STUFF(#columns,1,1,'')+')
) as pvt'
EXEC sp_executesql #sql
Output:
itemid pack1 UOM1 pack2 UOM2 pack3 UOM3
1 1 EA 10 BX 100 CA
2 1 EA 10 RL NULL NULL
EDIT#1
There is one more way:
DECLARE #sql nvarchar(max),
#columns nvarchar(max),
#colsrn nvarchar(max)
;WITH cte AS (
SELECT DISTINCT cast(ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY itemid) as nvarchar(max)) as rn
FROM #T1
)
SELECT #columns = (
SELECT ',MAX('+QUOTENAME('pack' + rn) +') as '+ QUOTENAME('pack' + rn) +',MAX('+
QUOTENAME('UOM'+ rn) +') as ' + QUOTENAME('UOM'+ rn)
FROM cte
FOR XML PATH('')
),
#colsrn = (
SELECT DISTINCT ',CASE WHEN rn = ' + rn +' THEN '+QUOTENAME('pack')+' ELSE NULL END as [pack'+rn+'],'
+'CASE WHEN rn = ' + rn +' THEN '+QUOTENAME('UOM')+' ELSE NULL END as [UOM'+rn+']'
FROM cte
FOR XML PATH('')
)
SELECT #sql = N'
SELECT itemid'+#columns+'
FROM (
SELECT itemid'+#colsrn+'
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY (SELECT NULL)) as rn
FROM #T1
) as t
) s
GROUP BY s.itemid'
EXEC sp_executesql #sql
Same output.
select itemid,
max(case when seq = 1 then pack end) pack1, max(case when seq = 1 then uom end)uom1,max(case when seq = 2 then pack end) pack2, max(case when seq = 2 then uom end) uom2,
max(case when seq = 3 then pack end) pack3, max(case when seq = 3 then uom end) uom3
from
(
select itemid, pack, uom, row_number() over(partition by itemid order by itemid) seq from T1
) d
group by itemid

how to use order by clause using Stuff in sql

stuff(( select distinct ',',Convert(varchar(256),y.RoomPlanID)
from(
select x.RoomPlanID ,x.RoomPlanHierarchy from (select utblConfigDestRoomRates.RoomPlanID,utblMstRoomPlans.RoomPlanHierarchy
FROM utblConfigDestRoomRates inner
JOIN utblMstRoomPlans on utblConfigDestRoomRates.RoomPlanID=utblMstRoomPlans.RoomPlanId
WHERE PropertyTypeID=1
AND BusinessID='AAAA'
AND('25-Aug-2015' BETWEEN DateEffective AND DateEnd)
AND StarRank=3 group by RoomPlanHierarchy order by RoomPlanHierarchy asc
) x
--order by
-- x.RoomPlanHierarchy
)y
FOR XML PATH('')), 1, 1, '')
You can use ROW_NUMBER (Transact-SQL) function. I assume that order to x.RoomPlanHierarchy like this
stuff(( select distinct ',',Convert(varchar(256),y.RoomPlanID)
from(
select x.RoomPlanID ,x.RoomPlanHierarchy,ROW_NUMBER() OVER(ORDER BY x.RoomPlanHierarchy asc) AS Row from (select utblConfigDestRoomRates.RoomPlanID,utblMstRoomPlans.RoomPlanHierarchy
FROM utblConfigDestRoomRates inner
JOIN utblMstRoomPlans on utblConfigDestRoomRates.RoomPlanID=utblMstRoomPlans.RoomPlanId
WHERE PropertyTypeID=1
AND BusinessID='AAAA'
AND('25-Aug-2015' BETWEEN DateEffective AND DateEnd)
AND StarRank=3 group by RoomPlanHierarchy
) x
--order by
-- x.RoomPlanHierarchy
)y
FOR XML PATH('')), 1, 1, '')
--Format it in SQL
(
SELECT
stuff(
(
SELECT
'' + a.col_name ,
'' + Replace(CAST(a.pos as VARCHAR), a.pos, '') + '"'
FROM
(
SELECT
CAST (',' + db_column_name + ' AS "' + REPLACE(label_tag, '.', '_') AS nvarchar (4000) ) AS col_name,
row_number() over (partition BY RIGHT(entity, CHARINDEX('.', (REVERSE(entity)) ) - 1) ORDER BY db_column_name) AS pos
FROM
table1 AS ex
JOIN
table2 AS ef
ON
ef.col1 = ex.col1
WHERE
entity LIKE '%string%')a FOR xml path('')) , 1, 1, '' ) )

Concatanating Pairs of Rows SQL Server [duplicate]

This question already has answers here:
Concatenate row values T-SQL
(15 answers)
Closed 7 years ago.
I have the following table, sorted.
ID Value Amount
1 A 10.00
2 B 4.25
3 C 2.01
4 D 5.00
How can I concatenate only consecutive pairs of rows and turn it to this:
ID Col1 Col2
1,2 A,B 10.00,4.25
3,4 C,D 2.01,5.00
And I don't want to use user-defined tables or temp tables. I am open to using
the window functions provided in SQL Server 2012 and 2014 though.
I looked at the other solutions and thought it was overkill, so I reused some and excluded or rewrote the unnecessary parts. This should result in better performance.
;WITH cte (rn, id, Value, Amount)
AS
(
SELECT ROW_NUMBER() OVER(ORDER BY id), id, Value, Amount
FROM yourtable
)
SELECT
( SELECT CAST(T.id AS VARCHAR(10)) + ','+ CAST(T1.id AS VARCHAR(10))
FROM cte AS T1
WHERE T1.rn = T.rn + 1) ID,
( SELECT CAST(T.value AS VARCHAR(10)) + ','+ CAST(T1.value AS VARCHAR(10))
FROM cte AS T1
WHERE T1.rn = T.rn + 1) COL1,
( SELECT CAST(T.Amount AS VARCHAR(10)) + ','+ CAST(T1.Amount AS VARCHAR(10))
FROM cte AS T1
WHERE T1.rn = T.rn + 1) COL2
FROM cte AS T
WHERE rn % 2 = 1
Try this,if it do not work with other sample data then let me know,
Declare #t table(ID varchar(50), Value varchar(50), Amount float)
insert into #t values(1,'A',10.00),(2,'B', 4.25),(3,'C',2.01)
,(4,'D',5.00 )
--Get the maxid
declare #MaxID int=(Select max(id) from #t)
;WITh CTE AS
(
SELECT ID,
STUFF((select ','+ id from #t where id IN(1,2) for xml path('')),1,1,'') [id1]
,STUFF((select ','+ Value from #t where id IN(1,2) for xml path('')),1,1,'') [Value]
,STUFF((select ','+ cast(Amount as varchar) from #t where id IN(1,2) for xml path('')),1,1,'') [Amount]
,1 RN
FROM #T a WHERE ID=1
UNION ALL
SELECT B.ID,
STUFF((select ','+ id from #t where id IN(RN+2,RN+3) for xml path('')),1,1,'')
,STUFF((select ','+ Value from #t where id IN(RN+2,RN+3) for xml path('')),1,1,'')
,STUFF((select ','+ cast(Amount as varchar) from #t where id IN(RN+2,RN+3) for xml path('')),1,1,'')
, RN+2
FROM cte a
CROSS APPLY(SELECT * FROM #T WHERE ID=RN+2 AND ID<=#MaxID) B
)
SELECT ID1,Value,Amount FROM CTE
This works just fine.. We just generate unique IDs for each pair using row_number and concatenate using FOR XML PATH on these IDs:
DECLARE #Test TABLE
(
ID VARCHAR(50)
, Value VARCHAR(50)
, Amount FLOAT
);
INSERT INTO #Test
(ID, Value, Amount)
VALUES
(1, 'A', 10.00)
, (2, 'B', 4.25)
, (3, 'C', 2.01)
, (4, 'D', 5.00);
;WITH cte (rn, id, Value, Amount)
AS
(
SELECT (ROW_NUMBER() OVER(ORDER BY id) + 1) / 2, id, Value, Amount
FROM #Test
)
SELECT DISTINCT
STUFF((SELECT ',' + CAST(T1.id AS VARCHAR(10))
FROM cte AS T1
WHERE T1.rn = T.rn
ORDER BY T1.id
FOR XML PATH('')), 1, 1, '') AS ID
, STUFF((SELECT ',' + CAST(T2.Value AS VARCHAR(10))
FROM cte AS T2
WHERE T2.rn = T.rn
ORDER BY T2.id
FOR XML PATH('')), 1, 1, '') AS Col1
, STUFF((SELECT ',' + CAST(T3.Amount AS VARCHAR(10))
FROM cte AS T3
WHERE T3.rn = T.rn
ORDER BY T3.id
FOR XML PATH('')), 1, 1, '') AS Col2
FROM cte AS T
Output:
ID Col1 Col2
-------------------
1,2 A,B 10,4.25
3,4 C,D 2.01,5

Resources