How can I convert a hierarchical table to xml in SQL - sql-server

I have a requirement to transform the result of a query into an xml fragment using t-SQL.
My table/query looks like this:
[Col1] [Col2] [Col3] qty
A group1 mod1 5
A group1 mod2 7
A group1 NULL 12
A group2 mod3 8
A group2 mod4 5
A group2 NULL 13
A NULL NULL 25
and the desired XML output should look like this
<A qty="25">
<group1 qty="12">
<mod1 qty="5"/>
<mod2 qty="7"/>
</group1>
<group2 qty="13">
<mod3 qty="8"/>
<mod4 qty="5"/>
</group2>
</A>
Is there a way to achieve this without pivoting the table and using dynamic sql which would be very messy

Good day, Please confirm that this solve your need. We can next work on improving performance. It is important to have the right indexes
DDL+DML
DROP TABLE IF EXISTS T
GO
CREATE TABLE T(
[Col1] VARCHAR(10),
[Col2] VARCHAR(10),
[Col3] VARCHAR(10),
qty VARCHAR(10)
)
GO
INSERT T([Col1],[Col2],[Col3],qty) VALUES
('A', 'group1', 'mod1', 5 ),
('A', 'group1', 'mod2', 7 ),
('A', 'group1', NULL , 12),
('A', 'group2', 'mod3', 8 ),
('A', 'group2', 'mod4', 5 ),
('A', 'group2', NULL , 13),
('A', NULL , NULL , 25)
GO
SELECT [Col1],[Col2],[Col3],qty
FROM T
GO
Solution
;with l1 as (
SELECT COL1, qty
FROM T
where col2 is null and col3 is null
)
, l2 as (
SELECT Col1, COL2, qty
FROM T
where col2 is not null and col3 is null
)
, l3 as (
SELECT Col1, COL2, COL3, qty
FROM T
where col2 is not null and col3 is not null
)
, a1 as (
SELECT COL2, X = STRING_AGG('<' + COL3 + ' qty="' + qty + '"/>' ,'')
FROM l3
GROUP BY Col2
)
, a2 as (
select X = '<' + a1.Col2 + ' qty="' + l2.qty + '">' + a1.X + '</' + a1.Col2 + '>', l2.Col1
from a1
LEFT JOIN l2 on a1.Col2 = l2.Col2
)
select CONVERT(XML,
'<' + l1.Col1 + ' qty="' + l1.qty + '">' + STRING_AGG(a2.X,'') + '</' + l1.Col1 + '>'
)
from a2
left join l1 on l1.Col1 = a2.Col1
group by l1.Col1, l1.qty
GO
Note! at the end I convert the value to XML in order to check that the result fit. In production there is no reason for this step, which cost resources.

#RonenAriely your solution inspired me. I've modified it a little. Because I could not run it.
My solution looks a little dirty and complicated and not elegant but the result is correct.
DECLARE #Tbl TABLE (Col1 CHAR(1), Col2 CHAR(6), Col3 CHAR(4), qty INT);
INSERT INTO #Tbl
VALUES ('A', 'group1', 'mod1', 5),
('A', 'group1', 'mod2', 7),
('A', 'group1', '', 12),
('A', 'group2', 'mod3', 8),
('A', 'group2', 'mod4', 5),
('A', 'group2', '', 13),
('A', '', '', 25);
WITH l1
AS (SELECT Col1,
Col2,
SUM(qty) AS qty FROM #Tbl WHERE Col2 <> ''
AND Col3 <> ''
AND Col2 <> ''
GROUP BY Col1,
Col2),
l2
AS (SELECT Col2,
SUM(qty) AS qty FROM #Tbl WHERE Col3 <> ''
GROUP BY Col2),
l3
AS (SELECT Col2,
Col3,
SUM(qty) AS qty FROM #Tbl
GROUP BY Col2,
Col3),
a1
AS (SELECT Col2,
'<' + Col3 + ' qty="' + CAST(qty AS VARCHAR(10)) + '"/>' AS c FROM l3 WHERE Col3 <> ''),
a2
AS (SELECT l1.Col1,
'<' + aa.Col2 + ' qty="' + CAST(l2.qty AS VARCHAR(10)) + '">' +
STUFF((SELECT DISTINCT '' + c FROM a1 WHERE Col2 = aa.Col2
FOR XML PATH ('')), 1, 0, '')
+ '</' + aa.Col2 + '>' AS c FROM a1 aa
LEFT JOIN l1
ON l1.Col2 = aa.Col2
LEFT JOIN l2
ON aa.Col2 = l2.Col2
GROUP BY l1.Col1,
aa.Col2,
l2.qty)
SELECT REPLACE(REPLACE(REPLACE('<' + aa.Col1 + ' qty="' + CAST(l1.qty AS VARCHAR(10)) + '">'
+ STUFF((SELECT DISTINCT '' + c FROM a2 WHERE Col1 = aa.Col1
FOR XML PATH ('')), 1, 0, '')
+ '</' + aa.Col1 + '>', '&', '&'), '<', '<'), '>', '>') AS c
FROM a2 aa
LEFT OUTER JOIN (SELECT Col1, SUM(qty) AS qty FROM l1 GROUP BY Col1) l1
ON l1.Col1 = aa.Col1
GROUP BY aa.Col1,
l1.qty;

Replying to my own post.
Thank you all for your help. In the end, I have implemented a solution using dynamic SQL as I have many blocks to process and I need a generic solution. My final code looks like this:
DECLARE #SQL nvarchar(4000)
SELECT #SQL = 'SELECT ' +
STUFF((SELECT ',' + qty + ' as '''
+ Col1
+ ISNULL('/'+ Col2,'')
+ ISNULL('/'+ Col3,'')
+ '/#qty''' FROM T ORDER BY Col1, Col2, Col3 FOR XML PATH(''))
,1,1,'') + ' FOR XML PATH('''')'
EXEC(#SQL)
Unfortunately, it cannot be implemented as a function (which was my initial goal) because it requires a physical table and because of the string size limitation. This would also not work for a large dataset with many sublevels. I will have to live with it!

Related

Data query using pivot encountered in SQL Server

I have a table with 3 nvarchar columns, 1 time column, and 2 columns Pass and Fail. I need to display the data by time column. On each milestone, there will be the number of Pass and Fail. I use Pivot and its only output Pass result without Fail. I tried everything. Please help
This is the input data:
Col1 Col2 Col3 Time Pass Fail
------------------------------------
A B C 08:30 80 0
A B C 09:30 60 2
A B C 10:30 80 0
A B C 11:30 70 0
I'm using this code:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT
#cols = STUFF((SELECT ',' + QUOTENAME(Time)
FROM Your_Table
GROUP BY Time
ORDER BY Time
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query = 'SELECT Col1,Col2,Col3,' + #cols + ' from
(
SELECT Col1,Col2,Col3,Time,Pass,Fail
from TD_SanLuong_CN
) x
pivot
(
sum(Pass)
for Time in (' + #cols + ')
) p1
pivot
(
sum(Fail)
for Time in (' + #cols + ')
) p2'
execute(#query);
Can I not use pivot to Fail?
I need output result:
Col1 Col2 Col3 08:30_Pass 08:30_Fail 09:30_Pass 09:30_Fail ...
A B C 80 0 60 2
Please help. Thank you!
Hi have a look at this code, i think this what you exactly needed as you expected Output
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
Drop table #Temp
CREATE TABLE #Temp (
Col1 CHAR(1),
Col2 CHAR(1),
Col3 CHAR(1),
[Time] TIME(0),
Pass INT,
Fail INT
);
INSERT #Temp (Col1, Col2, Col3, [Time], Pass, Fail) VALUES
('A', 'B', 'C', '08:30', 80, 0),
('A', 'B', 'C', '09:30', 60, 2),
('A', 'B', 'C', '10:30', 80, 0),
('A', 'B', 'C', '11:30', 70, 0);
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#cols2 AS NVARCHAR(MAX),
#cols3 AS NVARCHAR(MAX),
#Dyncols AS NVARCHAR(MAX)
SELECT
#cols = STUFF((SELECT ',' + QUOTENAME(Time)
FROM #Temp
GROUP BY [Time]
ORDER BY [Time]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SELECT
#cols2 = STUFF((SELECT ',' + 'MAX('+QUOTENAME(Time)+')' +' As '+ '['+CAST((Time) AS VARCHAR)+'_Pass'+']'
FROM #Temp
GROUP BY [Time]
ORDER BY [Time]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SELECT
#cols3 = STUFF((SELECT ',' + 'MAX('+QUOTENAME(Time)+')' +' As '+ '['+CAST((Time) AS VARCHAR)+'_Fail'+']'
FROM #Temp
GROUP BY [Time]
ORDER BY [Time]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SELECT
#Dyncols = STUFF((SELECT ',' + '['+CAST((Time) AS VARCHAR)+'_Pass'+']'+','+'['+CAST((Time) AS VARCHAR)+'_Fail'+']'
FROM #Temp
GROUP BY [Time]
ORDER BY [Time]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query=';with cte
AS
(
SELECT Col1,Col2,Col3,'+#cols2+' From
(
SELECT Col1,Col2,Col3,Time,Pass,Fail
FROM #Temp
) AS X
PIVOT
(
SUM(Pass)
FOR [Time] IN ('+#cols+' )
) p1
Group by Col1,Col2,Col3
),Cte2
AS
(
SELECT '+#cols3+' From
(
SELECT Col1,Col2,Col3,Time,Pass,Fail
from #Temp
) x
PIVOT
(
SUM(Fail)
FOR Time IN ('+#cols+')
)p1
Group by Col1,Col2,Col3
)
SELECT Col1,Col2,Col3,'+#Dyncols+' FROM cte ,Cte2'
PRINT #query
EXEC(#query)
Result
Col1 Col2 Col3 08:30:00_Pass 08:30:00_Fail 09:30:00_Pass 09:30:00_Fail 10:30:00_Pass 10:30:00_Fail 11:30:00_Pass 11:30:00_Fail
A B C 80 0 60 2 80 0 70 0

Count non-empty rows with unknown column names

Given a table that has unknown column names, how can I list all column names and the amount of rows that have a non-empty (NULL or empty string) value in that column?
Small example:
Col1 | Col2 | Col3 | dkasldk | dD? d3# !(
1 | | 2 | |
| 2 | d | ddd939 |
f | f | 84 | |
Should yield:
Column Name | Values
Col1 | 2
Col2 | 2
Col3 | 3
dkasldk | 1
dD? d3# !( | 0
I already tried around with using INFORMATION_SCHEMA.COLUMNS and creating dynamic SQL queries, but to no avail:
select N'select c.COLUMN_NAME,
(select sum(case when p.' + QUOTENAME(c.COLUMN_NAME) + ' != '''' then 1 else 0 end) from [Produkte] p) [Produkte]
from INFORMATION_SCHEMA.COLUMNS c
where c.TABLE_NAME = ''Produkte'' and c.COLUMN_NAME = ''' + c.COLUMN_NAME + '''' select_statement
from INFORMATION_SCHEMA.COLUMNS c
where c.TABLE_NAME = 'Produkte'
I cannot quite wrap my head around how to combine the two problems (for which I did find solution in their own, but not the combination) of Dynamic Column Names and Count empty values...
You can do something like this using UNPIVOT..
DECLARE #TableName NVARCHAR(MAX) = N'Produkte',
#CountColumns NVARCHAR(MAX),
#Columns NVARCHAR(MAX),
#Sql NVARCHAR(MAX)
SELECT #CountColumns = COALESCE(#CountColumns + ',', '') + 'COUNT(NULLIF(' + QUOTENAME(c.COLUMN_NAME) + ','''')) AS ' + QUOTENAME(c.COLUMN_NAME),
#Columns = COALESCE(#Columns + ',', '') + QUOTENAME(c.COLUMN_NAME)
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME = #TableName
SET #SQL = 'SELECT [Column Name], [Values]
FROM (
SELECT '
+ #CountColumns + '
FROM ' + #TableName + '
) t
UNPIVOT (
[Values]
FOR [Column Name] IN (' + #Columns + ')
) up
'
EXEC(#SQL)
SQL Fiddle Demo
You can use a dynamic UNPIVOT.
Using these variables:
DECLARE #qry NVARCHAR(MAX)
DECLARE #cols NVARCHAR(MAX) = ''
you can select columns names in a format suitable for the UNPIVOT operation with the following dynamic sql:
SELECT #cols = STUFF((SELECT ',[' + c.COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME = 'Produkte'
FOR XML PATH('')), 1, 1, '')
Finally use #cols to build the query:
SET #qry = 'SELECT t.Col, COUNT(*)
FROM (SELECT Val, Col
FROM Produkte
UNPIVOT (
Val FOR Col IN (' + #cols + ')) unpvt
) AS t
WHERE t.Val <> '''' OR t.Val IS NOT NULL
GROUP BY t.Col'
... and execute it to get desired result:
EXEC(#qry)
Ultimately the SQL you are after is something like this:
SELECT ColumnName, NonEmpty
FROM ( SELECT A = 1,
[Col1] = COUNT(CASE WHEN [Col1] <> '' THEN 1 END),
[Col2] = COUNT(CASE WHEN [Col2] <> '' THEN 1 END),
[Col3] = COUNT(CASE WHEN [Col3] <> '' THEN 1 END),
[dkasldk] = COUNT(CASE WHEN [dkasldk] <> '' THEN 1 END),
[dD? d3# !(] = COUNT(CASE WHEN [dD? d3# !(] <> '' THEN 1 END)
FROM #T
) AS t
UNPIVOT
( NonEmpty
FOR ColumnName IN ([Col1],[Col2],[Col3],[dkasldk],[dD? d3# !(])
) AS upvt;
UNPIVOT Will convert your data from
Col1 Col2 Col3
-------------------------
1 3 2
To the format you require:
ColumnName NonEmpty
-------------------------
Col1 1
Col2 3
Col3 2
You can build up dynamically using something like this:
-- SAMPLE DATA
USE TempDB;
CREATE TABLE #T
(
[Col1] VARCHAR(1),
[Col2] VARCHAR(1),
[Col3] VARCHAR(2),
[dkasldk] VARCHAR(6),
[dD? d3# !(] VARCHAR(1)
);
INSERT #T ([Col1], [Col2], [Col3], [dkasldk], [dD? d3# !(])
VALUES
('1', NULL, '2', NULL, ''),
('', '2', 'd', 'ddd939', NULL),
('f', 'f', '84', NULL, '');
DECLARE #TableName SYSNAME = '#T';
-- VALID INPUT TABLE
IF OBJECT_ID(#TableName, 'U') IS NULL
AND OBJECT_ID(#TableName, 'V') IS NULL
BEGIN
PRINT 'Invalid table or View';
RETURN
END
-- BUILD DYNAMIC SQL
DECLARE #SQL NVARCHAR(MAX) =
CONCAT('SELECT ColumnName, NonEmpty FROM (SELECT A = 1, ' ,
STUFF(( SELECT CONCAT(',',
QUOTENAME(name),
' = COUNT(CASE WHEN ',
QUOTENAME(Name),
' <> '''' THEN 1 END)')
FROM sys.columns
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, ''),
' FROM ',
#TableName,
') AS t UNPIVOT (NonEmpty FOR ColumnName IN (',
STUFF(( SELECT CONCAT(',', QUOTENAME(name))
FROM sys.columns
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, ''),
')) AS upvt');
-- EXECUTE DYNAMIC SQL
EXECUTE sp_executesql #SQL;

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

Select query to append columns with comma operator and ignoring nulls or empty values

We have a requirement of appending around three varchar columns of table with comma operator. Also we need to ignore the NULL or Empty Values during append. Suppose the table structure is as below
declare #t table (col1 varchar(10),col2 varchar(10),col3 varchar(10))
insert into #t
select 'test1','test2','test3' union all
select 'test4',null,'test5' union all
select null,null,'test6' union all
select '','test7',''
Expected result must be as below
Result
--------------------------------
test1,test2,test3
test4,test5
test6
test7
I have tried using case statements, but the query is becoming very complex. BTW we are using SQL Server 2005. Looking for any easy and simple solution. Thanks in advance.
Not very elegant;
;with T(string) as (
select
isnull(case col1 when '' then null else col1 + ',' end, '')
+ isnull(case col2 when '' then null else col2 + ',' end, '')
+ isnull(case col3 when '' then null else col3 + ',' end, '')
from #t
)
select left(string, abs(len(string) - 1)) from T
Here is a very alternative way
select reverse(stuff(reverse(coalesce(replace(col1, col1, col1 + ','), '')+
coalesce(replace(col2, col2, col2 + ','), '')+
coalesce(replace(col3, col3, col3 + ','), '')), 1,1,'')) Result from #t
Basically, just append a comma to each column if that column is not null / empty.Then, strip the last comma off of the result.
SELECT
CASE
WHEN RIGHT(T.outColumn, 1) = ',' THEN SUBSTRING(T.outColumn, 1, LEN(T.outColumn) - 1)
ELSE T.outColumn
END
FROM
(
SELECT
ISNULL(col1, '') + CASE WHEN ISNULL(col1, '') <> '' THEN ',' ELSE '' END +
ISNULL(col2, '') + CASE WHEN ISNULL(col2, '') <> '' THEN ',' ELSE '' END +
ISNULL(col3, '') AS outColumn
FROM
#t
) AS T

SQL FOR XML PATH list and COUNT

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....

Resources