This requirment could be resolved by PIVOT. Since the count of columns is dynamic, it has to use dynamic SQL.
The first step just like you thought, JOIN the DateTable and Order first, then the steps following:
- Build a PIVOT table named with D, which fill order_id in each row of date.
- Build another PIVOT table named with T, which fill time in each row of date.
- Join D and T on their date, concat order_id and time like the format as you shown.
- Convert the SQL to a dynamic SQL:
- Get the max count of orders in a day.
- Build a string for PIVOT columns and assign to variant p_cols.
- Build a string for output columns and assign to variant o_cols.
- Replace 3 parts of SQL with those 2 variants.
- EXECUTE the SQL
Code is here:
--Create a test table of order
CREATE TABLE [#Order]
(
order_id varchar(4) primary key,
[date] date,
[time] time
)
GO
INSERT INTO [#Order]
Values
('1001', '2024/10/01', '08:00')
,('1002', '2024/10/01', '10:00')
,('1003', '2024/10/01', '12:00')
,('1004', '2024/10/03', '07:00')
,('1005', '2024/10/04', '16:00')
,('1006', '2024/10/04', '20:00')
,('1007', '2024/10/01', '14:00')
GO
DECLARE @max_cols INT --Max count of orders in a day
DECLARE @p_cols NVARCHAR(MAX) = N'' --string of pivot columns
DECLARE @o_cols NVARCHAR(MAX) = N'' --string of output columns
DECLARE @n INT
DECLARE @c_n NVARCHAR(1)
DECLARE @SQL NVARCHAR(MAX) = N'' --SQL to execute
--Get the max count of oders in a day
SELECT TOP(1) @max_cols = COUNT(order_id) FROM [#Order] GROUP BY [date] ORDER BY COUNT(order_id) DESC
--build p_cols and o_cols
SET @n = 1
WHILE (@n <= @max_cols)
BEGIN
SET @c_n = CAST(@n AS NVARCHAR)
SET @p_cols += N'[' + @c_n + N']'
SET @o_cols += (N'ISNULL(D.[' + @c_n + '] + ''-'' + T.[' + @c_n + '], N'''') AS [Order ' + @c_n + ']')
IF @n < @max_cols
BEGIN
SET @p_cols += N', '
SET @o_cols += N', '
END
SET @n += 1
END
--debug output
--PRINT @p_cols
--PRINT @o_cols
--Build SQL string
SET @SQL =
'WITH DateTable AS
(
SELECT DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0) AS [date]
UNION ALL
SELECT DATEADD(DAY, 1, [date])
FROM DateTable
WHERE DATEADD(DAY, 1, [date]) <= EOMONTH(GETDATE())
)
SELECT D.DaysOfMonth, '
+
@o_cols
+' FROM
(
SELECT
CAST(d.[date] AS date) DaysOfMonth
, O.order_id
, ROW_NUMBER() OVER(PARTITION BY O.[date] ORDER BY O.[time]) AS SN
FROM DateTable as d
LEFT JOIN [#Order] O ON d.[date] = O.[date]
)S
PIVOT
(
MAX(order_id) FOR SN IN ('
+
@p_cols
+')
)D
INNER JOIN
(
SELECT * FROM
(
SELECT
CAST(d.[date] AS date) DaysOfMonth
, FORMAT(O.[time], N''hh\:mm'') AS [Time]
, ROW_NUMBER() OVER(PARTITION BY O.[date] ORDER BY O.[time]) AS SN
FROM DateTable as d
LEFT JOIN [#Order] O ON d.[date] = O.[date]
)S
PIVOT
(
MAX([Time]) FOR SN IN ('
+
@p_cols
+')
)TT
)T
ON D.DaysOfMonth = T.DaysOfMonth
'
--debug output
--PRINT @SQL
--Execute dynamic SQL to get result
EXECUTE(@SQL)
-------------------------------------------------------------------------------------------
--SQL pattern to debug, after get correct result, copy to assign @SQL, replace output column list and 2 PIVOT column lists with variants
--WITH DateTable AS
--(
-- SELECT DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0) AS [date]
-- UNION ALL
-- SELECT DATEADD(DAY, 1, [date])
-- FROM DateTable
-- WHERE DATEADD(DAY, 1, [date]) <= EOMONTH(GETDATE())
--)
--SELECT D.DaysOfMonth,
--ISNULL(D.[1] + '-' + T.[1], N'') AS [Order 1], ISNULL(D.[2] + '-' + T.[2], N'') AS [Order 2], ISNULL(D.[3] + '-' + T.[3], N'') AS [Order 3]
-- FROM
--(
-- SELECT
-- CAST(d.[date] AS date) DaysOfMonth
-- , O.order_id
-- , ROW_NUMBER() OVER(PARTITION BY O.[date] ORDER BY O.[time]) AS SN
-- FROM DateTable as d
-- LEFT JOIN [#Order] O ON d.[date] = O.[date]
--)S
--PIVOT
--(
-- MAX(order_id) FOR SN IN (
--[1],[2],[3]
--)
--)D
--INNER JOIN
--(
-- SELECT * FROM
-- (
-- SELECT
-- CAST(d.[date] AS date) DaysOfMonth
-- , FORMAT(O.[time], N'hh\:mm') AS [Time]
-- , ROW_NUMBER() OVER(PARTITION BY O.[date] ORDER BY O.[time]) AS SN
-- FROM DateTable as d
-- LEFT JOIN [#Order] O ON d.[date] = O.[date]
-- )S
-- PIVOT
-- (
-- MAX([Time]) FOR SN IN (
--[1],[2],[3]
--)
-- )TT
--)T
--ON D.DaysOfMonth = T.DaysOfMonth
DROP TABLE [#Order]