T-SQL PIVOT on one column to append the others - sql

Given this table (there's always 2 duplicates of Combinations):
Combination Variable Value
-----------------------------
0 a 1
0 b 2
1 c 3
1 d 4
2 e 5
2 f 6
...
I want to query it to get this:
Variable 0 Value 0 Variable 1 Value 1 Variable 2 Value 2 ...
---------------------------------------------------------------------------
a 1 c 3 e 5
b 2 d 4 f 6
I've tried using PIVOT with dynamic query but cannot come up with a satisfying result.
Can somebody please advise?
EDIT: though Ullas solution works perfectly for combination pairs, I was wondering if it's possible to achieve the same result with combination N-uplets (e.g. (0, 0, 0), (1, 1, 1), (2, 2, 2) should result in 3 rows)? I reckon dynamic query is still the way to go, maybe with PIVOT this time.

Use dynamic sql.
I just created one. Don't know how efficient it is.
Query
declare #query1 varchar(max);
declare #query2 varchar(max);
select #query1 = 'select ' +
STUFF
(
(
select distinct
',min(t.Variable' + cast(Combination as varchar(6)) + ') as Variable' +
cast(Combination as varchar(6)) +
',min(t.Value' + cast(Combination as varchar(6)) + ') as Value' +
cast(Combination as varchar(6))
from tblComb
for xml path('')
),
1,1,'');
select #query1 += ' from('
select #query1 += 'select '+
stuff
(
(
select distinct
',max(case when Combination = ' + cast(Combination as varchar(6)) +'
then Variable end) as Variable' + cast(Combination as varchar(6)) +
',max(case when Combination = ' + cast(Combination as varchar(6)) +'
then Value end) as Value' + cast(Combination as varchar(6))
from tblComb
for xml path('')
),
1, 1, '');
select #query1 += ' from tblComb group by Combination, Variable)t union all ';
select #query2 = 'select ' +
STUFF
(
(
select distinct
',max(t.Variable' + cast(Combination as varchar(6)) + ') as Variable' +
cast(Combination as varchar(6)) +
',max(t.Value' + cast(Combination as varchar(6)) + ') as Value' +
cast(Combination as varchar(6))
from tblComb
for xml path('')
),
1, 1, '');
select #query2 += ' from('
select #query2 += 'select '+
stuff
(
(
select distinct
',max(case when Combination = ' + cast(Combination as varchar(6)) +'
then Variable end) as Variable' + cast(Combination as varchar(6)) +
',max(case when Combination = ' + cast(Combination as varchar(6)) +'
then Value end) as Value' + cast(Combination as varchar(6))
from tblComb
for xml path('')
),
1, 1, '');
select #query2 += ' from tblComb group by Combination, Variable)t;';
select #query1 += #query2;
execute(#query1);
Sample table
+-------------+----------+-------+
| Combination | Variable | Value |
+-------------+----------+-------+
| 0 | a | 1 |
| 0 | b | 2 |
| 1 | c | 3 |
| 1 | d | 4 |
| 2 | e | 5 |
| 2 | f | 6 |
+-------------+----------+-------+
Result set
+-----------+--------+-----------+--------+-----------+--------+
| Variable0 | Value0 | Variable1 | Value1 | Variable2 | Value2 |
+-----------+--------+-----------+--------+-----------+--------+
| a | 1 | c | 3 | e | 5 |
| b | 2 | d | 4 | f | 6 |
+-----------+--------+-----------+--------+-----------+--------+

Related

How to pivot a table this way when there are 3000 columns

I am using SQL Server
I have a table TT that looks like this
TargetID RowID Actual
0001 1 0
0001 2 1
0001 3 1
0002 1 0
0002 2 1
0002 3 0
0003 1 1
0003 2 1
0003 3 0
How can I pivot it is to this
RowID Target0001 Target0002 Target0003
1 0 0 1
2 1 1 1
3 1 0 0
I tried
SELECT 'TargetID' + TargetID, RowID, Actual
FROM TT
WHERE TargetID = '0001'
UNION ALL
SELECT 'TargetID' + TargetID, RowID, Actual
FROM TT
WHERE TargetID = '0002'
SELECT 'TargetID' + TargetID, RowID, Actual
FROM TT
WHERE TargetID = '0003'
But there are 3000 TargetIDs and my method is not good for that
Any idea how to do that?
You can try this...
DECLARE #ColumnsTable TABLE ([ColumnName] VARCHAR(50));
INSERT INTO #ColumnsTable ([ColumnName])
SELECT DISTINCT '[' + CONVERT(VARCHAR(48), [TargetID]) + ']'
FROM TT;
DECLARE #PivotColumns VARCHAR(MAX), #TotalColumn VARCHAR(MAX), #SQL VARCHAR(MAX);
SET #PivotColumns = (SELECT STUFF((SELECT DISTINCT ', ' + CONVERT(VARCHAR(50), [ColumnName])
FROM #ColumnsTable
FOR XML PATH('')), 1, 2, ''));
SET #SQL = 'SELECT RowID,' +#PivotColumns +'
FROM (
SELECT RowID,TargetID,Actual
FROM TT) AS t
PIVOT (MAX([Actual])
FOR [TargetID] IN (' + #PivotColumns + ')) AS p';
EXEC(#SQL);

SQL - Getting Percent Difference from dynamic SQL

I currently have a dynamic stored procedure that takes every month in my database and averages out the values per day.
What I currently have (values are the overall average for that month):
+-------------------------------------------------------+
| ID | CustName | 201501 | 201502 | 201503 | 201504 | ..|
+-------------------------------------------------------+
| 32 | CustOne | 5852.25| 5847.50| 6542.98| 7585.25| ..|
| 56 | CustTwo | 5452.45| 7852.50| 6985.41| 1245.21| ..|
| 89 | CustThree| 8520.25| 7410.01| 9630.36| 1245.32| ..|
| .. | ... | .. | .. | .. | .. | ..|
+-------------------------------------------------------+
This is the stored procedure I'm using to create the above table:
DECLARE #Dates NVARCHAR(MAX);
SELECT #Dates = CONCAT(#Dates + ', ', QUOTENAME(BalMonth))
FROM vAvgMonBal
GROUP BY BalMonth
ORDER BY BalMonth;
DECLARE #DynSQL NVARCHAR(MAX),
#months NVARCHAR(MAX);
SET #months = 'CONCAT( CONVERT(nvarchar(15), YEAR(BalDate)) , IIF(LEN(MONTH(BalDate)) > 1, CONVERT(nvarchar(15), MONTH(BalDate)), ''0'' + CONVERT(nvarchar(15), MONTH(BalDate)))) AS BalMonth'
SET #DynSQL = 'SELECT *
FROM
(SELECT
a1.IDNbr,
a2.CustName, ' + #months + ',
AVG(a1.Balance) as Balance
FROM tblID a1
INNER JOIN tblCust a2 ON (a1.IDNbr = a2.IDNbr)
WHERE a2.CustType != ''Inactive''
GROUP BY
a1.IDNbr, a2.CustName, CONCAT( CONVERT(nvarchar(15), YEAR(BalDate)) , IIF(LEN(MONTH(BalDate)) > 1, CONVERT(nvarchar(15), MONTH(BalDate)), ''0'' + CONVERT(nvarchar(15), MONTH(BalDate))))) as d1
PIVOT (
AVG(Balance)
FOR BalMonth IN (' + #Dates + ')
) piv';
EXECUTE sp_executesql #DynSQL
Question: how can I take the data from the previous stored procedure and get the percent difference, (Month1/Month2) * 100, from it like shown below? I'm expecting I'll need a new stored procedure or add on to the one I currently have.
What I'm needing (where each "PerDiff" is the percent difference of the previous month from the before table example):
+---------------------------------------------------------------+
| ID | CustName | PerDiff1 | PerDiff2 | PerDiff3 | PerDiff4 | ..|
+---------------------------------------------------------------+
| 32 | CustOne | 100.00 | 68.12 | 654.25 | 483.36 | ..|
| 56 | CustTwo | 58.21 | 154.54 | 932.45 | 58.45 | ..|
| 89 | CustThree| 965.25 | 951.58 | 689.12 | 32.50 | ..|
| .. | ... | .... | .... | .... | .... | ..|
+---------------------------------------------------------------+
I've attempted to use something like:
DECLARE #PerDiff nvarchar(max);
SET #PerDiff = 'SELECT *
FROM (' + #DynSQL + ')'
EXECUTE sp_executesql #PerDiff
To at least try and get the data moving.
I'm no longer getting any error messages-- I'm generally just stuck on how to proceed with getting the math to be applied dynamically.
Any help or advice would be greatly appreciated!
EDIT1: Here is the result of finalized #DynSQL
SELECT *
FROM
(
SELECT a1.DDANbr,
a2.CustName,
CONCAT( CONVERT(nvarchar(15), YEAR(BalDate)) , IIF(LEN(MONTH(BalDate)) > 1, CONVERT(nvarchar(15), MONTH(BalDate)), '0' + CONVERT(nvarchar(15), MONTH(BalDate))))AS BalMonth,
a1.Balance
FROM tblID a1 INNER JOIN tblCust a2 ON (a1.IDNbr = a2.IDNbr)
WHERE a2.CustType != 'Inactive'
GROUP BY a1.IDNbr, a2.CustName, BalDate, a1.Balance
) as d1
PIVOT (
AVG(Balance)
FOR BalMonth IN ([201501], [201502], [201503], [201504], [201505], [201506], [201507], [201508], [201509], [201510], [201511], [201512], [201601], [201602], [201603], [201604], [201605], [201606], [201607], [201608], [201609], [201610], [201611], [201612], [201701], [201702], [201703], [201704], [201705], [201706], [201707], [201708], [201709], [201710], [201711], [201712], [201801], [201802], [201803], [201804], [201805], [201806], [201807], [201808], [201809])
) piv
When you take a query and make it a derived table, you have to give that table an alias.
Like this:
DECLARE #PerDiff nvarchar(max);
SET #PerDiff = 'SELECT IDNbr, CustName, ' + #months + '
FROM (' + #DynSQL + ') t1'
EXECUTE sp_executesql #DynSQL, #PerDiff
DECLARE #Dates NVARCHAR(MAX);
SELECT #Dates = CONCAT(#Dates + ', ', QUOTENAME(BalMonth))
FROM vAvgMonBal
GROUP BY BalMonth
ORDER BY BalMonth;
DECLARE #DynSQL NVARCHAR(MAX),
#months NVARCHAR(MAX);
SET #months = 'CONCAT( CONVERT(nvarchar(15), YEAR(BalDate)) , IIF(LEN(MONTH(BalDate)) > 1, CONVERT(nvarchar(15), MONTH(BalDate)), ''0'' + CONVERT(nvarchar(15), MONTH(BalDate)))) AS BalMonth'
SET #DynSQL = 'SELECT *
FROM
(SELECT
a1.IDNbr,
a2.CustName, ' + #months + ',
AVG(a1.Balance) as Balance
FROM tlbID a1
INNER JOIN tblCust a2 ON (a1.IDNbr = a2.IDNbr)
WHERE a1.Balance != 0.00
AND a2.CustType != ''Inactive''
GROUP BY
a1.IDNbr, a2.CustName, CONCAT( CONVERT(nvarchar(15), YEAR(BalDate)) , IIF(LEN(MONTH(BalDate)) > 1, CONVERT(nvarchar(15), MONTH(BalDate)), ''0'' + CONVERT(nvarchar(15), MONTH(BalDate))))) as d1
PIVOT (
AVG(Balance)
FOR BalMonth IN (' + #Dates + ')
) piv';
EXECUTE sp_executesql #DynSQL

How to group mean values of a column after pivoting in SQL Server

I have the following lines of SQL code which are part of a stored procedure:
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME([vw_Imported_Files].RF_FileName)
FROM [dbo].[vw_Imported_Files] with(nolock)
WHERE [vw_Imported_Files].RF_GUID_ID = #sGUID
AND [vw_Imported_Files].RF_IsEnabled = 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query2 = 'SELECT ROUND(AVG(CAST(RD_Axis AS FLOAT)),3) AS RD_Axis
,' + #cols + ' FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY DenseRank ORDER BY (SELECT NULL))
AS RowNumber, rd.* FROM
(SELECT DENSE_RANK() OVER (ORDER BY rd.RD_RF_ID) AS DenseRank
,rd.RD_Axis AS RD_Axis
,rd.DN_Values AS DN_Values
,rd.RF_FileName AS RF_FileName
FROM #TEMP rd
WHERE rd.RD_GUID_ID = ' + #sGUID + '
AND rd.RD_IsEnabled = 1
) rd
) rn
pivot
(
max(DN_Values)
for RF_FileName in (' + #cols + ')
) p
GROUP BY RD_Axis, ' + #cols + ' '
execute (#query2)
Actually, this query (#query2) results as shown in the following table.
---------------------------------------------------------------
| 0 | NULL | NULL | 0,996573652935408|
| 0 | NULL | 1,00053003751428 | NULL |
| 0 | 0,999843071844672 | NULL | NULL |
| 0,052 | NULL | NULL | 0,992999630825293|
| 0,052 | 1,02368347072563 | NULL | NULL |
| 0,053 | NULL | 0,992674427713489 | NULL |
| 0,104 | NULL | NULL | 0,998690236570867|
| 0,104 | NULL | 0,996645964692132 | NULL |
| 0,105 | 0,989815140503533 | NULL | NULL |
----------------------------------------------------------------------------
What I want to do, is to clear all the NULL values by grouping the Average of the RD_Axis values that have the same DenseRank value. In the current instance, the DenseRank increases every 3 rows. So, the outcome that I want has to look like as depicted in the next table.
----------------------------------------------------------------------------
| 0 | 0,999843071844672 | 1,00053003751428 | 0,996573652935408|
| 0,052 | 1,02368347072563 | 0,992674427713489 | 0,992999630825293|
| 0,104 | 0,989815140503533 | 0,996645964692132 | 0,998690236570867|
----------------------------------------------------------------------------
I hope to find my inquiry clear. Could you please help me to figure out how to transform the above SQL code in order for me to get the desirable result?
You might need to create a new column variable that gets the MAX(dynamicColumn) value for your outer select.. that way you can remove the GROUP BY at the end.
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME([vw_Imported_Files].RF_FileName)
FROM [dbo].[vw_Imported_Files] with(nolock)
WHERE [vw_Imported_Files].RF_GUID_ID = #sGUID
AND [vw_Imported_Files].RF_IsEnabled = 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #maxCols = STUFF((SELECT distinct ', MAX(' + QUOTENAME([vw_Imported_Files].RF_FileName) + ') AS ' + QUOTENAME([vw_Imported_Files].RF_FileName)
FROM [dbo].[vw_Imported_Files] with(nolock)
WHERE [vw_Imported_Files].RF_GUID_ID = #sGUID
AND [vw_Imported_Files].RF_IsEnabled = 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query2 = '
SELECT ROUND(AVG(CAST(RD_Axis AS FLOAT)),3) AS RD_Axis
,' + #maxCols + '
FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY DenseRank ORDER BY (SELECT NULL))
AS RowNumber, rd.*
FROM (SELECT DENSE_RANK() OVER (ORDER BY rd.RD_RF_ID) AS DenseRank
,rd.RD_Axis AS RD_Axis
,rd.DN_Values AS DN_Values
,rd.RF_FileName AS RF_FileName
FROM #TEMP rd
WHERE rd.RD_GUID_ID = ' + #sGUID + ' AND rd.RD_IsEnabled = 1
) rd
) rn
pivot
(
max(DN_Values)
for RF_FileName in (' + #cols + ')
) p
GROUP BY DenseRank'
execute (#query2)
This can probably be done without using PIVOT and be a lot cleaner by using aggregates with CASE expressions
Actually I got the result that I was looking for with the following code.
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME([vw_Imported_Files].RF_FileName)
FROM [dbo].[vw_Imported_Files] with(nolock)
WHERE [vw_Imported_Files].RF_GUID_ID = #sGUID and [vw_Imported_Files].RF_IsEnabled = 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #maxCols = STUFF((SELECT distinct ', MAX(' + QUOTENAME([vw_Imported_Files].RF_FileName) + ') AS ' + QUOTENAME([vw_Imported_Files].RF_FileName)
FROM [dbo].[vw_Imported_Files] with(nolock)
WHERE [vw_Imported_Files].RF_GUID_ID = #sGUID AND [vw_Imported_Files].RF_IsEnabled = 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query2 = '
SELECT ' + #maxCols + '
FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY DenseRank ORDER BY (SELECT NULL))
AS RowNumber, rd.*
FROM (SELECT DENSE_RANK() OVER (ORDER BY rd.RD_RF_ID) AS DenseRank
,rd.RD_Axis AS RD_Axis
,rd.DN_Values AS DN_Values
,rd.RF_FileName AS RF_FileName
FROM #TEMP rd
WHERE rd.RD_GUID_ID = ' + #sGUID + ' AND rd.RD_IsEnabled = 1
) rd
) rn
pivot
(
max(DN_Values)
for RF_FileName in (' + #cols + ')
) p
GROUP BY RowNumber ORDER BY RowNumber'
execute (#query2)
At first I generated the outcome that I wanted (#query2) and then I got the average of the RD_Axis values with another query (both ordered by Rownumber asc). Then I linked them with a UNION ALL statement. Btw, thanks for the fresh perspective on my issue because I had been stuck for hours.

Turning rows into columns over a many-to-many table

So I have three tables:
Students:
StuID
------
1
2
StuCon:
StuConId StuID ConID StuConType Priority
----------------------------------------------
1 1 1 Parent 1
2 1 2 Guardian 2
3 2 3 Parent 1
Contacts:
ConID ConName ConPhn
----------------------
1 John 5555555
2 Sally 4444444
3 Dana 3333333
I'm trying to get results that look like this:
StuID ConID1 StuConType1 ConName1 ConPhone1 ConID2 StuConType2 ConName2 ConPhone2
----------------------------------------------------------------------------------------
1 1 Parent John 5555555 2 Guardian Sally 4444444
2 3 Parent Dana 3333333 Null Null Null Null
So far the only way I can figure to do that is by making a LOT of left joins (some students have up to 10 contacts so that's 10 left joins for stucon and 10 more for contacts)
I'm quite sure there is a pivot that can be applied here, I just can't figure out how to do it.
Here's one way to do this dynamically:
DECLARE #selects VARCHAR(MAX) = '', #SQL VARCHAR(MAX) = '';
SELECT #selects += '
, MAX(CASE WHEN SC.[Priority] = ' + CAST([Priority] AS VARCHAR(255)) + ' THEN C.ConID END) [ConID' + CAST([Priority] AS VARCHAR(255)) + ']
, MAX(CASE WHEN SC.[Priority] = ' + CAST([Priority] AS VARCHAR(255)) + ' THEN SC.StuConType END) [StuConType' + CAST([Priority] AS VARCHAR(255)) + ']
, MAX(CASE WHEN SC.[Priority] = ' + CAST([Priority] AS VARCHAR(255)) + ' THEN C.ConName END) [ConName' + CAST([Priority] AS VARCHAR(255)) + ']
, MAX(CASE WHEN SC.[Priority] = ' + CAST([Priority] AS VARCHAR(255)) + ' THEN C.ConPhn END) [ConPhone' + CAST([Priority] AS VARCHAR(255)) + ']'
FROM StuCon
GROUP BY [Priority]
ORDER BY [Priority];
SET #SQL = 'SELECT StuID' + #selects + ' FROM StuCon SC LEFT JOIN Contacts C ON C.ConID = SC.ConID GROUP BY StuID;';
EXEC(#SQL);
Notes: It probably should be a normal JOIN on contacts rather than an LEFT JOIN, but it's a left join here just in case there's some inconsistency between tables. The Students table hasn't been joined to because it's not necessary.

CrossTab Query / Pivot Table in MS SQL?

I have a table that has the follow data structure:
terminal | load_time_mns | vehicle
_________________________________________
Terminal 1 | 3 | AA
Terminal 2 | 10 | AF
Terminal 1 | 1 | BF
Terminal 6 | 3 | QRS
Terminal 6 | 1.4 | AA
Terminal 3 | 2.5 | OP
I am trying to get an interval breakdown of load time from each terminal.For example, for the above table, I am trying to create a breakdown that looks like the following:
terminal | [0-1 mns] | [1-2 mns] | [2-3 mns] |
_______________________________________________________________
Terminal 1 | 0 | 1 | 1
_______________________________________________________________
Terminal 2 | 0 | 0 | 0
_______________________________________________________________
Terminal 3 | 0 | 0 | 1
_______________________________________________________________
Terminal 6 | 0 | 1 | 1
After a bit of Googling, it looks like I should be focusing on the pivot() function and crosstab queries. I am reading up on those two, but am still not quite able to get
Something like this might help:
Query 1:
SELECT
terminal,
count(CASE WHEN load_time_mns >= 0 AND load_time_mns < 1 THEN 1 END) [0-1 mns],
count(CASE WHEN load_time_mns >= 1 AND load_time_mns < 2 THEN 1 END) [1-2 mns],
count(CASE WHEN load_time_mns >= 2 AND load_time_mns < 3 THEN 1 END) [2-3 mns]
FROM t
GROUP BY terminal
Results:
| TERMINAL | 0-1 MNS | 1-2 MNS | 2-3 MNS |
|------------|---------|---------|---------|
| Terminal 1 | 0 | 1 | 0 |
| Terminal 2 | 0 | 0 | 0 |
| Terminal 3 | 0 | 0 | 1 |
| Terminal 6 | 0 | 1 | 0 |
Fiddle here.
Note that in your example you did not include 1 in the [0-1] range but you did include 3 in the [0-3] range, which seems not right.
You can use a bit of dynamic SQL to extend this to the complete result set. Create a table called intervals to store the intervals:
Create Table ex (
terminal varchar(10),
load_time_mns decimal(10, 2),
vehicle varchar(3)
);
Insert Into ex values
('Terminal 1', 3, 'AA'),
('Terminal 2', 10, 'AF'),
('Terminal 1', 1, 'BF'),
('Terminal 6', 3, 'QRS'),
('Terminal 6', 1.4, 'AA'),
('Terminal 3', 2.5, 'OP');
Create Table intervals (
min_mns decimal(10, 2),
max_mns decimal(10, 2),
column_name sysname
);
declare #i int = 0
while #i <= 20
begin
insert into intervals values (
#i, #i + 1, convert(varchar, #i) + '-' + convert(varchar, #i + 1)
);
set #i += 1;
end
while #i <= 420
begin
insert into intervals values (
#i, #i + 5, convert(varchar, #i) + '-' + convert(varchar, #i + 5)
);
set #i += 5;
end
You can then use a cursor to build up the complete SQL
declare
#sql nvarchar(max) = N'select terminal',
#lo int, #hi int, #col sysname;
declare pivot_cursor cursor local fast_forward for
select
min_mns, max_mns, column_name
from
intervals
order by
min_mns;
open pivot_cursor;
fetch next from pivot_cursor into #lo, #hi, #col;
while ##fetch_status = 0
begin
set #sql += ', sum(case when load_time_mns >= ' + convert(varchar, #lo)
+ ' and load_time_mns < ' + convert(varchar, #hi)
+ ' then 1 else 0 end) as [' + #col + ']';
fetch next from pivot_cursor into #lo, #hi, #col;
end
close pivot_cursor;
deallocate pivot_cursor;
Set #sql += ' from ex group by Terminal order by terminal';
exec sp_executesql #sql;
Example SQLFiddle
DECLARE #t TABLE ( terminal VARCHAR(10), load_time_mns DECIMAL(5,2), vehicle VARCHAR(3))
INSERT INTO #t ( terminal, load_time_mns, vehicle )
VALUES
('Terminal 1' , 3 , 'AA'),
('Terminal 2' , 10 , 'AF'),
('Terminal 2' , 20 , 'AF'),
('Terminal 1' , 1 , 'BF'),
('Terminal 1' , 25 , 'BF'),
('Terminal 6' , 3 , 'QRS'),
('Terminal 6' , 1.4 , 'AA'),
('Terminal 3' , 2.5 , 'OP')
;WITH intervals AS
(
SELECT m = 0, n = 1
UNION ALL
SELECT CASE WHEN m < 20 THEN m+1 ELSE m+5 END, CASE WHEN n < 20 THEN n+1 ELSE n+5 END
FROM intervals
WHERE n<420
)
SELECT terminal, load_time_mns, vehicle,
interval = CAST(m AS VARCHAR(10)) + '-' + CAST(n AS VARCHAR(10)), m
INTO ##tmp
FROM intervals i
LEFT JOIN #t t
ON t.load_time_mns >= i.m
AND t.load_time_mns < i.n
OPTION (MAXRECURSION 0)
DECLARE #cols VARCHAR(MAX) =
STUFF(CAST((SELECT ',' + QUOTENAME(interval)
FROM (
SELECT DISTINCT interval, m
FROM ##tmp
) t
ORDER BY m
FOR XML PATH(''), TYPE
) AS VARCHAR(MAX)),1,1,'')
DECLARE #sql VARCHAR(MAX) = '
SELECT terminal, ' + #cols + '
FROM (
SELECT terminal, vehicle, interval
FROM ##tmp
) t
PIVOT (
COUNT(vehicle)
FOR interval IN (' + #cols + ')
) p
'
EXEC(#sql)
DROP TABLE ##tmp