SQL - Getting Percent Difference from dynamic SQL - 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

Related

Convert rows to column by date with pivot

I have read the stuff on MS pivot tables and I am still having problems getting this correct.
Data
wh_id | saledate | qty |
105 | 20190901 | 134.000000 |
105 | 20190902 | 190.000000 |
105 | 20190903 | 148.500000 |
105 | 20190904 | 157.500000 |
105 | 20190905 | 209.500000 |
I would like it to come out as a pivot table, like this:
wh_id | 1 | 2 | 3 | 4 | 5 |
105 | 134 | 190 |148.5 | 157.5 | 209.5 |
this the code :
DECLARE
#cols nvarchar(max)='' ,
#query nvarchar(max)=''
SET #cols = STUFF((SELECT ',' + QUOTENAME(DATEPART(dd, saledate))
FROM sales
WHERE month(saleDATE)=9 and year(saleDATE)=2019 and wh_id=105
GROUP BY saledate
ORDER BY saledate ASC
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'');
set #query = 'SELECT [wh_id], ' + #cols + '
from
(
select [wh_id], QUOTENAME(DATEPART(dd, saledate)) saledate,qty
from sales where month(saleDATE)=9 and year(saleDATE)=2019 and wh_id=105
) x
pivot
(
sum(qty)
for [saledate] in (' + #cols + ')
) p '
execute(#query);
but the result is like this
wh_id | 1 | 2 | 3 | 4 | 5 |
105 | 1 | 2 | 3 | 4 | 5 |
just change the source part from the above query remove QUOTENAME applied over saledate and try executing the query you will find the expected output.
set #query = 'SELECT [wh_id], ' + #cols + '
from
(
select [wh_id], DATEPART(dd, saledate) saledate,qty
from sales where month(saleDATE)=9 and year(saleDATE)=2019 and wh_id=105
) x
pivot
(
sum(qty)
for [saledate] in (' + #cols + ') ) p '
select wh_id,
max(case when rn = 1 then qty end) '1',
max(case when rn = 2 then qty end) '2',
max(case when rn = 3 then qty end) '3',
max(case when rn = 4 then qty end) '4',
max(case when rn = 5 then qty end) '5'
from
(
select wh_id,qty,
row_number() over(partition by wh_id order by qty) rn
from YourTableName
) src
group by wh_id
OutPut:-
Note:- Instead of using Pivot ...you can use this simple query...using ...case when and then with max aggregate function...
In above example....i'm forgot add decimal value in Table....

How to use pivot in two or more columns in SQL Server

This is my sample table
Item Stocks Sold Year
------------------------------------------------------
Shoes 30 5 2018
Slippers 15 15 2019
Sandals 20 10 2016
Pants 25 5 2018
Shoes 20 0 2017
What I'm trying to achieve is that the columns of Stocks and Sold will be posted by Year.
Example output :-
As of 2016 | As of 2017 | As of 2018 | As of 2019
----------------+------+--------+------+--------+------+---------+-------
Item Stocks | Sold | Stocks | Sold | Stocks | Sold | Stocks | Sold
----------------+------+--------+------+--------+------+---------+--------
Shoes | | 20 | 0 | 30 | 5 | |
Slippers | | | | | | 15 | 15
Sandals 20 | 10 | | | | | |
Pants | | | | 25 | 5 | |
Glad for any help :)
Try this:-
declare #Stock nvarchar(max)
(Select #Stock = Stuff( (select distinct ',[Stock_' + cast(year as varchar(5)) + ']' from Stock for xml path('')), 1,1,''))
declare #Sold nvarchar(max)
(Select #Sold = Stuff( (select distinct ',[Sold_' + cast(year as varchar(5)) + ']' from Stock for xml path('')), 1,1,''))
declare #col1 nvarchar(max)
(Select #col1 = Stuff( (select distinct ',cts.[Stock_' + cast(year as varchar(5)) + ']' + ',ctd.[Sold_' + cast(year as varchar(5)) + ']' from Stock for xml path('')), 1,1,''))
declare #query nvarchar(max) = '
; with cte as (
select Item, stock, ''Stock'' + ''_'' + Cast(year as varchar(10)) as colstock from stock)
, ct as (
select Item, Sold, ''Sold'' + ''_'' + Cast(year as varchar(10)) as colsold from stock)
, ctstock as (
select * from
( select item, stock, colstock from cte )
as d
pivot
( max(stock) for colstock in (' + #Stock + ') )
as p
)
, ctsold as (
select * from
( select item, sold, colsold from ct )
as d
pivot
( max(sold) for colsold in (' + #Sold + ') )
as p
)
select cts.item, ' + #col1 + ' from ctstock as cts inner join ctsold as ctd on cts.item = ctd.item'
exec sp_executesql #query
use conditional aggregate
SELECT Item,
SUM (CASE WHEN Year = 2016 THEN Stocks END) as Stock2016,
SUM (CASE WHEN Year = 2016 THEN Sold END) as Sold2016
FROM yourtable
GROUP BY Item

SQL Server Pivot on multiple fields

I have searched this website for all possible solutions but still can't find an answer for my Pivot problem.
I have a table with the following data.
Portfolio | Date | TotalLoans | ActiveLoans | TotalBalance
--------------------------------------------------------------------
P1 | 2015-12-31 | 1,000 | 900 | 100,000.00
P1 | 2015-11-30 | 1,100 | 800 | 100,100.00
P1 | 2015-10-31 | 1,200 | 700 | 100,200.00
I am trying to create a pivot with the following output (only where Portfolio = P1)
Field | 2015-12-31 | 2015-11-30 | 2015-10-31 |
-----------------------------------------------------
TotalLoans | 1,000 | 1,100 | 1,200 |
ActiveLoans | 900 | 800 | 700 |
TotalBalance | 100,000 | 100,100 | 100,200 |
Ideally, I am looking for a dynamic pivot, but a static query would do as well and I can try a dynamic query out of that.
You need first to UNPIVOT your table. You can do it using this query:
SELECT Portfolio, [Date], Val, ColType
FROM (SELECT Portfolio,
[Date],
TotalLoans,
ActiveLoans,
TotalBalance
FROM mytable
WHERE Portfolio = 'P1') AS srcUnpivot
UNPIVOT (
Val FOR ColType IN (TotalLoans, ActiveLoans, TotalBalance)) AS unpvt
Output:
Portfolio Date Val ColType
===============================================
P1 2015-12-31 1000 TotalLoans
P1 2015-12-31 900 ActiveLoans
P1 2015-12-31 100000 TotalBalance
P1 2015-11-30 1100 TotalLoans
P1 2015-11-30 800 ActiveLoans
P1 2015-11-30 100100 TotalBalance
P1 2015-10-31 1200 TotalLoans
P1 2015-10-31 700 ActiveLoans
P1 2015-10-31 100200 TotalBalance
Note: All unpivoted fields must be of the same type. The query above assumes a type of int for all fields. If this is not the case then you have to use CAST.
Using the above query you can apply PIVOT:
SELECT Portfolio, ColType, [2015-12-31], [2015-11-30], [2015-10-31]
FROM (
... above query here ...
PIVOT (
MAX(Val) FOR [Date] IN ([2015-12-31], [2015-11-30], [2015-10-31])) AS pvt
This is Giorgos Betsos solution as dynamic SQL. This will deal without the need to write the date values explicitly.
Please: If you like this: Do not mark this solution as accepted, set the acceptance to Giorgos Betsos. There's the hard work! But you may vote on it :-)
CREATE TABLE #tbl(Portfolio VARCHAR(10),[Date] DATE,TotalLoans DECIMAL(10,4),ActiveLoans DECIMAL(10,4),TotalBalance DECIMAL(10,4));
INSERT INTO #tbl VALUES
('P1','20151231',1000,900,100000.00)
,('P1','20151130',1100,800,100100.00)
,('P1','20151031',1200,700,100200.00);
DECLARE #pvtColumns VARCHAR(MAX)=
(
STUFF(
(
SELECT DISTINCT ',['+CONVERT(VARCHAR(10), [Date] ,126) + ']'
FROM #tbl
FOR XML PATH('')
)
,1,1,'')
);
DECLARE #cmd VARCHAR(MAX)=
'SELECT Portfolio, ColType, ' + #pvtColumns +
' FROM (
SELECT Portfolio, [Date], Val, ColType
FROM (SELECT Portfolio,
[Date],
TotalLoans,
CAST(ActiveLoans AS DECIMAL(10,4)) AS ActiveLoans,
TotalBalance
FROM #tbl AS mytable
WHERE Portfolio = ''P1'') AS srcUnpivot
UNPIVOT (
Val FOR ColType IN (TotalLoans, ActiveLoans, TotalBalance)) AS unpvt
) AS srcPivot
PIVOT (
MAX(Val) FOR [Date] IN (' + #pvtColumns + ')) AS pvt';
EXEC (#cmd);
You need to use Dyanmic SQL and construct them. See examples at http://social.technet.microsoft.com/wiki/contents/articles/17510.t-sql-dynamic-pivot-on-multiple-columns.aspx
Here is the code
CREATE procedure CrossTab
(
#select varchar(2000),
#PivotCol varchar(100),
#Summaries varchar(100),
#GroupBy varchar(100),
#OtherCols varchar(100) = Null
)
AS
set nocount on
set ansi_warnings off
declare #sql varchar(8000)
Select #sql = ''
Select #OtherCols= isNull(', ' + #OtherCols,'')
create table #pivot_columns (pivot_column_name varchar(100))
Select #sql='select ''' + replace( + #PivotCol,',',''' as pivot_column_name union all select ''')+''''
insert into #pivot_columns
exec(#sql)
select #sql=''
create table #pivot_columns_data (pivot_column_name varchar(100),pivot_column_data varchar(100))
Select #PivotCol=''
Select #PivotCol=min(pivot_column_name) from #pivot_columns
While #PivotCol>''
Begin
insert into #pivot_columns_data(pivot_column_name,pivot_column_data)
exec
(
'select distinct ''' + #PivotCol +''' as pivot_column_name, convert(varchar(100),' + #PivotCol + ') as pivot_column_data from
('+
#select
+'
) T'
)
Select #PivotCol=min(pivot_column_name) from #pivot_columns where pivot_column_name>#PivotCol
end
select
#sql = #sql + ', ' +
replace(
replace(
#Summaries,'(','(CASE WHEN ' + Pivot_Column_name + '=''' +
pivot_column_data + ''' THEN '
),
')[', ' END) as [' + pivot_column_data
)
from #pivot_columns_data
order by pivot_column_name
exec
(
'select ' + #GroupBy +#OtherCols +#sql +
' from (
'+
#select
+'
) T
GROUP BY ' + #GroupBy
)
drop table #pivot_columns
drop table #pivot_columns_data
set nocount off
set ansi_warnings on
Usage
EXEC CrossTab
'SELECT LastName, OrderDate,shipcountry FROM northwind..Employees Employees
INNER JOIN northwind..Orders Orders ON (Employees.EmployeeID=Orders.EmployeeID) ',
'shipcountry,Year(OrderDate)',
'Count(LastName)[]',
'LastName'
Do it in several steps:
if object_id('tempdb..#Data') is null
CREATE TABLE #Data
([Portfolio] varchar(2), [Date] datetime,
[TotalLoans] decimal(9,2), [ActiveLoans] int, [TotalBalance] decimal(9,2))
;
INSERT INTO #Data
([Portfolio], [Date], [TotalLoans], [ActiveLoans], [TotalBalance])
VALUES
('P1', '2015-12-31', 1000, 900, 100000.00),
('P1', '2015-11-30', 1100, 800, 100100.00),
('P2', '2015-10-31', 1200, 700, 100200.00)
;
WITH Transposed AS (
--First reorganise the data, creating unions like this, by column
--Assumption is that you are not interested in [Portfolio]
SELECT [Portfolio], [Date], [TotalLoans] AS Amount, 'TotalLoans' Field FROM #Data
UNION SELECT [Portfolio], [Date], [ActiveLoans], 'ActiveLoans' FROM #Data
UNION SELECT [Portfolio], [Date], [TotalBalance], 'TotalBalance' FROM #Data
)
SELECT Field, [2015-10-31], [2015-11-30], [2015-12-31] --You can build a string with all the dates from your original data source
FROM (
SELECT [Date], [Field], [Amount] FROM Transposed
) d
PIVOT (
MAX(Amount)
FOR [Date] IN ([2015-10-31], [2015-11-30], [2015-12-31])
) p

How to pivot the given structure to expected one?

I have queries results as in the image, similarly for 90 date's, so how to group object and make date's as columns and count to respective date's.
Thanks in advance!!
You can do it using a dynamic crosstab:
SQL Fiddle
DECLARE #sql1 VARCHAR(4000) = ''
DECLARE #sql2 VARCHAR(4000) = ''
DECLARE #sql3 VARCHAR(4000) = ''
SELECT #sql1 =
'SELECT
[Object]' + CHAR(10)
SELECT #sql2 = #sql2 +
' , MAX(CASE WHEN [Date] = CAST(''' + CONVERT(VARCHAR(8), [Date], 112) + ''' AS DATE) THEN [count] END) AS ' + QUOTENAME([Date]) + CHAR(10)
FROM(
SELECT DISTINCT [Date] FROM tbl
)t
ORDER BY [Date]
SELECT #sql3 =
'FROM tbl
GROUP BY [Object]
ORDER BY [Object]'
PRINT(#sql1 + #sql2 + #sql3)
EXEC (#sql1 + #sql2 + #sql3)
RESULT
| Object | 2015-01-01 | 2015-01-02 |
|--------|------------|------------|
| 1 | 10 | 34 |
| 2 | 20 | 46 |
| 3 | 130 | 78 |
| 4 | 40 | 89 |
| 5 | 55 | 45 |
This is the output of the PRINT command:
SELECT
[Object]
, MAX(CASE WHEN [Date] = CAST('20150101' AS DATE) THEN [count] END) AS [2015-01-01]
, MAX(CASE WHEN [Date] = CAST('20150102' AS DATE) THEN [count] END) AS [2015-01-02]
FROM tbl
GROUP BY [Object]
ORDER BY [Object]
You can use SQL Server PIVOT relational operator
DECLARE #SQLQuery AS NVARCHAR(MAX)
DECLARE #PivotColumns AS NVARCHAR(MAX)
--Get unique values of pivot column
SELECT #PivotColumns= COALESCE(#PivotColumns + ',','') + QUOTENAME([Date])
FROM (SELECT DISTINCT [Date] FROM [dbo].[PivotExample]) AS PivotExample
--Create the dynamic query with all the values for
--pivot column at runtime
SET #SQLQuery =
N'SELECT ObjectId, ' + #PivotColumns + '
FROM [dbo].[PivotExample]
PIVOT( SUM(COUNT)
FOR [Date] IN (' + #PivotColumns + ')) AS P'
EXEC sp_executesql #SQLQuery

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