I have a table with 7 columns:
Start_hour | Start_minute | Start_second | End_hour | End_minute | end_second | date
My task is to sum the differences between start and end times on current day. And my query already does that. Sadly I need the output to be in minutes:seconds only. For Example the total sum of time differences is 2 hour 26 minutes and 52 seconds.
I need my output to like this:
126:52
My query right now looks like this:
SELECT
RIGHT(CONVERT(CHAR(8),
DATEADD(SECOND,
SUM(DATEDIFF(SECOND,
Timefromparts(start_hour, start_minute, start_second, 0, 0),
Timefromparts(end_hour, end_minute, end_second, 0, 0))
), 0), 108), 5)
FROM
opoznienia
WHERE
YEAR(data) = YEAR(GETDATE())
AND MONTH(data) = MONTH(GETDATE())
AND DAY(data) = DAY(GETDATE())
Sample data:
Start_hour | Start_minute | Start_second | End_hour | End_minute | end_second | date
10 15 0 10 30 30 2018-11-27 14:40:53.680
10 15 0 10 30 30 2018-11-30 10:16:20.610
6 10 30 6 23 45 2018-12-02 01:00:27.243
8 10 0 8 53 45 2018-12-02 14:42:48.663
10 5 13 10 55 23 2018-12-02 14:53:03.560
Output of the query above:
47:13 (without RIGHT command it would be 01:47:13)
The wanted output:
107:13
You need to calculate the number of seconds and then format it as you want. From number of seconds you can calculate the minutes as division by 60, where the remainder are the seconds:
declare #NumberOfSeconds int = 7612
-- Returns 126:52
select concat(#NumberOfSeconds / 60, ':', FORMAT(#NumberOfSeconds % 60, 'D2'))
Here is one option:
SELECT
numSeconds,
CASE WHEN numSeconds / 60 <= 100
THEN RIGHT('00' + CONVERT(VARCHAR(20), numSeconds / 60), 2)
ELSE CONVERT(VARCHAR(20), numSeconds / 60) END
+ ':' +
CASE WHEN numSeconds / 60 <= 100
THEN RIGHT('00' + CONVERT(VARCHAR(20), numSeconds % 60), 2)
ELSE CONVERT(VARCHAR(20), numSeconds % 60) END AS output
FROM yourTable;
Demo
The ugliness in the code has to do with that you expect a minimum of two digits for the minute and second components. So, we have to pad each component with zeroes in the case where minutes or seconds happens to be just a single digit.
The other answers are better, but to understand what may be missing in what you're trying to do - you can look at following where DatePart is used.
-->"
..(without RIGHT command it would be 01:47:13)
" This is your clue that you that you need to work with the parts and not the whole...
SELECT Cast
(
Datepart
(
hour,
DATEADD(second,sum(datediff(second,
TIMEFROMPARTS ( Start_hour, Start_minute, Start_second, 0, 0),
TIMEFROMPARTS ( End_hour, End_minute, End_second, 0, 0))),0)
) * 60
+
Datepart
(
minute,
DATEADD(second,sum(datediff(second,
TIMEFROMPARTS ( Start_hour, Start_minute, Start_second, 0, 0),
TIMEFROMPARTS ( End_hour, End_minute, End_second, 0, 0))),0)
) As varchar)
+
':'
+
Cast
(
Datepart
(
second,
DATEADD(second,sum(datediff(second,
TIMEFROMPARTS ( Start_hour, Start_minute, Start_second, 0, 0),
TIMEFROMPARTS ( End_hour, End_minute, End_second, 0, 0))),0)
) As varchar) as result
from minsec
Try the following
CREATE TABLE T(
StartHour INT,
StartMinute INT,
StartSecond INT,
EndHour INT,
EndMinute INT,
EndSecond INT,
[Date] DATE
);
INSERT INTO T VALUES
(10, 15, 0 , 10, 30, 30, '2018-11-27'),
(10, 15, 0 , 10, 30, 30, '2018-11-30'),
(6 , 10, 30, 6 , 23, 45, '2018-12-02'),
(8 , 10, 0 , 8 , 53, 45, '2018-12-02'),
(10, 5 , 13, 10, 55, 23, '2018-12-02');
SELECT *,
CAST( (DATEDIFF(Hour, StartTime, EndTime) * 60) +
(DATEDIFF(Minute, StartTime, EndTime) % 60) AS VARCHAR
) + ':' +
CAST(DATEDIFF(Second, StartTime, EndTime) % 60 AS VARCHAR)
FROM
(
SELECT [Date],
TIMEFROMPARTS(StartHour, StartMinute, StartSecond, 0, 0) StartTime,
TIMEFROMPARTS(EndHour, EndMinute, EndSecond, 0, 0) EndTime
FROM T
) TT
You can also SUM() and GROUP BY [Date] if you want to.
SELECT [Date],
CAST( SUM( (DATEDIFF(Hour, StartTime, EndTime) * 60) +
(DATEDIFF(Minute, StartTime, EndTime) % 60)
) AS VARCHAR
) + ':' +
CAST(SUM(DATEDIFF(Second, StartTime, EndTime) % 60) AS VARCHAR)
FROM
(
SELECT [Date],
TIMEFROMPARTS(StartHour, StartMinute, StartSecond, 0, 0) StartTime,
TIMEFROMPARTS(EndHour, EndMinute, EndSecond, 0, 0) EndTime
FROM T
) TT
GROUP BY [Date]
Demo
UPDATE
It seems like you are looking for
SELECT [Date],
CAST(SUM(DATEDIFF(Second, StartTime, EndTime)) / 60 AS VARCHAR) + ':' +
CAST(SUM(DATEDIFF(Second, StartTime, EndTime)) % 60 AS VARCHAR) [MM:SS]
FROM
(
SELECT [Date],
TIMEFROMPARTS(StartHour, StartMinute, StartSecond, 0, 0) StartTime,
TIMEFROMPARTS(EndHour, EndMinute, EndSecond, 0, 0) EndTime
FROM T
) TT
GROUP BY [Date];
Returns:
+---------------------+--------+
| Date | MM:SS |
+---------------------+--------+
| 27/11/2018 00:00:00 | 15:30 |
| 30/11/2018 00:00:00 | 15:30 |
| 02/12/2018 00:00:00 | 107:10 |
+---------------------+--------+
Demo
Related
I am trying to calculate shipping performance in SSMS- Im getting stuck in several areas and I hope i can get some help!
I have an Estimated Ship Date, an Appt Date, and an Actual Ship date.
Im measuring warehouse performance, so in most cases shipments leave same day. But in others, they may leave a few days early or late.
The problem that i am having is the correct output. I want to show the Values in DD:HH:MM, but the syntax for DateDiff doesn't give me an accurate day to use:
For Example, a shipment was supposed to leave on 6/3/2019 # at 8 am, but didnt leave the warehouse until 7/22/2019 # 6:30 AM. In this case, DateDiff calcs 49 days, when really its 48 days 22 hours and 30 minutes late. Here is an example of some of the syntax i am using:
EstimatedShipDate datetime,
AppointmentShipDate datetime,
ActualShipDate datetime
);
insert into #test values ('2019-07-01 11:00', '2019-07-01 11:00','2019-06-30 10:30');
insert into #test values ('2019-07-08 13:45', null,'2019-07-01 22:00');
insert into #test values ('2019-07-09 15:00', null,'2019-07-10 15:00');
insert into #test values ('2019-07-03 15:00', null,'2019-07-04 15:00');
insert into #test values ('2019-07-08 15:00', null,'2019-07-08 15:00');
insert into #test values ('2019-07-08 15:00', null,'2019-07-08 22:00');
insert into #test values ('2019-07-03 08:00', null,'2019-07-04 15:00');
insert into #test values ('2019-07-03 08:00', null,'2019-07-03 06:30');
insert into #test values ('2019-06-03 08:00', null,'2019-07-22 06:30');
insert into #test values ('2019-07-01 11:00', null,'2019-06-29 10:30');
Select
EstimatedShipDate,
AppointmentShipDate,
ActualShipDate,
DATEDIFF(DAY,ISNULL(CAST(AppointmentShipDate as DateTime),CAST(EstimatedShipDate as DateTime)), CAST(ActualShipDate as DateTime)) as Days,
DATEPART(DAY, ISNULL(CAST(AppointmentShipDate as DateTime),CAST(EstimatedShipDate as DateTime))-CAST(ActualShipDate as DateTime) ) as days2,
DATEDIFF(Hour,ISNULL(CAST(AppointmentShipDate as DateTime),CAST(EstimatedShipDate as DateTime)), CAST(ActualShipDate as DateTime)) as Hours,
convert(varchar, CAST(ActualShipDate as DateTime)-ISNULL(CAST(AppointmentShipDate as DateTime),CAST(EstimatedShipDate as DateTime)),108) as DateSubtract_Convert,
convert(varchar,ISNULL(CAST(AppointmentShipDate as DateTime),CAST(EstimatedShipDate as DateTime)-CAST(ActualShipDate as DateTime) ),108) as DateSubtract_ConvertEarly
from #TEST
In this case, DateDiff calcs 49 days, when really its 48 days 22 hours and 30 minutes late. Or vice versa, I have a date range that shows 2 days 23:20 but it should be 1 day 23:20
One way is to increase granularity of DATEDIFF to MINUTE. That will require calculation for the days, hours, and minutes using the minutes (division and mod).
SELECT ABS(dT.diff_Minutes / 1440) AS [Days] --days, there are 1440 minutes in a day
,ABS((dT.diff_Minutes % 1440) / 60) AS [Hours] --hours remaining in the day
,ABS((dT.diff_Minutes % 1440) % 60) AS [Minutes] --minutes remaining in the day
FROM (
SELECT DATEDIFF(MINUTE, ISNULL(AppointmentShipDate, EstimatedShipDate), ActualShipDate) [diff_Minutes]
FROM #test
) AS dT
Your temp table produces output:
Days Hours Minutes
1 0 30
6 15 45
1 0 0
1 0 0
0 0 0
0 7 0
1 7 0
0 1 30
48 22 30
2 0 30
To put these in DD:HH:MM format is more complicated, but you can cast the numbers to varchar and concatenate as strings. RIGHT is used to add any leading zeroes.
SELECT CASE WHEN dT.diff_Minutes < 0 THEN '- ' ELSE '+ ' END --positive or negative
+ RIGHT('00' + CAST(ABS(dT.diff_Minutes / 1440) as varchar(1000)), 2)
+ ':'
+RIGHT('00' + CAST((ABS(dT.diff_Minutes % 1440) / 60) as varchar(2)), 2)
+ ':'
+RIGHT('00' + CAST((ABS(dT.diff_Minutes % 1440) % 60) as varchar(2)), 2)
AS [DD:HH:MM]
FROM (
SELECT DATEDIFF(MINUTE, ISNULL(AppointmentShipDate, EstimatedShipDate), ActualShipDate) [diff_Minutes]
FROM #test
) AS dT
Produces output:
DD:HH:MM
- 01:00:30
- 06:15:45
+ 01:00:00
+ 01:00:00
+ 00:00:00
+ 00:07:00
+ 01:07:00
- 00:01:30
+ 48:22:30
- 02:00:30
Use seconds instead of days and then do a little math with seconds, minutes and hours.
Here's a simple SSMS example:
DECLARE
#ScheduledDate DATETIME = '6/3/2019 08:00:00',
#ShippedDate DATETIME = '07/22/2019 06:30:00';
SELECT
CONVERT( VARCHAR, DATEDIFF( s, #ScheduledDate, #ShippedDate ) /60/60/24 ) + ' Days and '
+ CONVERT( VARCHAR, ( #ShippedDate - #ScheduledDate ), 108 ) + ' Hours.';
Returns
48 Days and 22:30:00 Hours.
I am having some difficulty in trying to figure out something, lets say I have a date and time;
And I want to add 180 minutes to it so;
SELECT DATEADD(MINUTE,180,'2018-05-24 15:00')
This would give me answer of "2018-05-24 18:00" but I want to do it in a range so ADD the minutes if you are between 09:00 - 17:00 so something like this;
SELECT DATEADD(MINUTES,180,'2018-05-24 15:00') WHERE '2018-05-24 15:00' BETWEEN '2018-05-24 09:00' AND '2018-05-24 17:00'
So the answer to this would be "2018-05-25 10:00"
Was hard, but this should work for all your cases. This solution works for any amount of (positive) minutes and result will always be inside the parametrized hours, adding the corresponding amount of days.
DECLARE #RangeHourStart INT = 9
DECLARE #RangeHourEnd INT = 17
DECLARE #MinutesToAdd INT = 120
DECLARE #Date DATETIME = '2018-05-24 15:00'
SELECT
FinalDate = CASE
WHEN -- When final hour exceeds the range hour
DATEPART(HOUR, #Date) * 60 +
DATEPART(MINUTE, #Date) +
#MinutesToAdd % ((#RangeHourEnd - #RangeHourStart) * 60) > #RangeHourEnd * 60
THEN
DATEADD(HOUR, -1 * (#RangeHourStart - 1),
DATEADD(DAY, 1,
DATEADD(MINUTE, #MinutesToAdd % ((#RangeHourEnd - #RangeHourStart) * 60),
DATEADD(
DAY,
#MinutesToAdd / ((#RangeHourEnd - #RangeHourStart) * 60),
#Date))))
ELSE
DATEADD(MINUTE, #MinutesToAdd % ((#RangeHourEnd - #RangeHourStart) * 60),
DATEADD(
DAY,
#MinutesToAdd / ((#RangeHourEnd - #RangeHourStart) * 60),
#Date))
END
I made it so you don't need to hard-code any value.
This doesn't look particularly pretty, however...
USE Sandbox;
GO
CREATE TABLE Times (DateNTime datetime2(0));
INSERT INTO Times
VALUES ('20180520 10:00:00'),
('20180520 15:20:00'),
('20180521 09:32:00'),
('20180521 14:17:00'),
('20180522 16:54:00'),
('20180523 12:46:00'),
('20180524 15:32:00');
GO
SELECT *
FROM Times;
GO
SELECT T.DateNTime,
CASE WHEN CONVERT(time,T.DateNTime) <= '14:00' THEN DATEADD(MINUTE, 180,T.DateNTime)
ELSE DATEADD(MINUTE, 180 - DATEDIFF(MINUTE,T.DateNTime,DATEADD(HOUR,17,DATEADD(DAY, DATEDIFF(DAY, 0, T.DateNTime),0))), DATEADD(HOUR,9,DATEADD(DAY, DATEDIFF(DAY, 0, T.DateNTime) + 1,0))) END
FROM Times T;
GO
DROP TABLE Times;
you can try this:
DECLARE #input DATETIME='2018-05-24 15:00'
DECLARE #min INT=180
SELECT CASE WHEN DATEADD(MINUTE,#min,#input)>DATEADD(HOUR, 17,DateAdd(Day, Datediff(Day,0, #input), 0))
THEN DATEADD(MINUTE,
DATEDIFF(MINUTE,
DATEADD(HOUR, 17,
DATEADD(Day,
DATEDIFF(Day,0, #input),
0)
),
DATEADD(MINUTE,#min,#input)),
DATEADD(Hour,9,
DATEADD(Day,1,
DateAdd(Day,
Datediff(Day,0, #input),
0)
)
)
)
ELSE DATEADD(MINUTE,#min,#input)
END
I have a column in a table that stores the number of minutes as a numeric(18,4) field named [course_access_minutes].
The stored values come from a blackboard database and look like this:
0.0500
0.0667
0.3667
up to
314.0833
625.8167
How do I convert these to time hh:mm, I've had a good look at the database documentation and all I can find is
course_access_minutes numeric(18,4) This is the number of minutes that the user accesses this course in total during this login session.
Can I assume that I can make a direct conversion from minutes into hours? I think I will take any values below 1 as 0 minutes. What is the best way to do this in SQL? Thanks in advance for your help.
Try this
SELECT CONVERT(varchar, DATEADD(s, 625.8167 * 60, 0), 108)
If the duration is longer than 24 hours you can use this
SELECT CONVERT(varchar, CAST(1877.4501 * 60 AS int) / 3600)
+ RIGHT(CONVERT(varchar, DATEADD(s, 1877.4501 * 60, 0), 108), 6)
You could use FLOOR like this
DECLARE #SampleData AS TABLE
(
Minutes numeric(18,4)
)
INSERT INTO #SampleData
VALUES
( 0.0500),
( 1.0500),
( 30.0500),
( 80.0500),
( 314.0833),
( 625.8167)
SELECT CONCAT(floor(sd.Minutes/60),':', CASE WHEN sd.Minutes - floor(sd.Minutes/60)*60 < 1 THEN '0'
ELSE FLOOR(sd.Minutes - floor(sd.Minutes/60)*60 )
END) AS hours
FROM #SampleData sd
Returns
hours
0:0
0:1
0:30
1:20
5:14
10:25
WITH _Samples AS (
SELECT CONVERT(numeric(18, 4), 0.0500) [course_access_minutes]
UNION ALL SELECT 0.0667
UNION ALL SELECT 0.3667
UNION ALL SELECT 314.0833
UNION ALL SELECT 625.8167
)
SELECT
S.course_access_minutes,
-- split out the number
FLOOR(S.course_access_minutes / 60) [hours],
FLOOR(S.course_access_minutes % 60) [minutes],
FLOOR((S.course_access_minutes - FLOOR(S.course_access_minutes)) * 60) [seconds],
-- to a string
CONVERT(varchar(10), FLOOR(S.course_access_minutes / 60))
+ ':' + RIGHT('00' + CONVERT(varchar(10), FLOOR(S.course_access_minutes % 60)), 2)
+ ':' + RIGHT('00' + CONVERT(varchar(10), FLOOR((S.course_access_minutes - FLOOR(S.course_access_minutes)) * 60)), 2) [time_string],
-- You could consider converting to the time data type if the values will never exceed the limit
-- time supports 00:00:00.0000000 through 23:59:59.9999999
-- 0 through 1439.9833333 ... 23 * 60 = 1380 + 59 = 1439 + (59 / 60) = 1439.9833333
-- (see: https://learn.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql)
CONVERT(time,
CONVERT(varchar(10), FLOOR(S.course_access_minutes / 60))
+ ':' + RIGHT('00' + CONVERT(varchar(10), FLOOR(S.course_access_minutes % 60)), 2)
+ ':' + RIGHT('00' + CONVERT(varchar(10), FLOOR((S.course_access_minutes - FLOOR(S.course_access_minutes)) * 60)), 2)
) [time]
FROM
_Samples S
(It wouldn't be difficult to further this idea and split out the fractional seconds as well.)
Which yields:
course_access_minutes hours minutes seconds time_string time
---------------------- ------ -------- -------- ------------ ----------------
0.0500 0 0 3 0:00:03 00:00:03.0000000
0.0667 0 0 4 0:00:04 00:00:04.0000000
0.3667 0 0 22 0:00:22 00:00:22.0000000
314.0833 5 14 4 5:14:04 05:14:04.0000000
625.8167 10 25 49 10:25:49 10:25:49.0000000
Note that this is going to be like Greg's answer, but I wanted to explain and simplify it.
You have minutes, so dividing them by 60 and flooring it (removing the decimal) gives the hours (without the minutes).
If you take the total minutes again, and remove (mod it by) the floored hours - which requires conversion to minutes by multiplying by 60 - you are left with the remaining minutes by essentially just finding out what is left after taking away that many groups of sixties:
SELECT FLOOR(course_access_minutes / 60) as Hours,
(FLOOR(course_access_minutes) % 60) as Minutes
FROM MyTable
If you want the decimal to appear for the amount of minute fractions (you want the seconds to appear, in decimal form), remove FLOOR.
If you want seconds in real numbers, keep FLOOR and use what Greg had: FLOOR((S.course_access_minutes - FLOOR(S.course_access_minutes)) * 60) for seconds. Be careful with the parenthesis, though, because you can end up accidentally flooring your decimaled minutes and get 0, and then 0*60 is zero:
FLOOR(
(
course_access_minutes -
FLOOR(course_access_minutes)
) * 60
) as Seconds
In a table, I have a column (AvgWaitTime) which stores data value in seconds (Data Type: float), date wise. I have a function which performs some calculation using AvgWaitTime column and other few columns and returns a value in time format. I would to convert the value returned by the function (time format) into seconds (preferrably decimal, if not then int).
select
(datepart(HH, dbo.fnGetMonthlyAverageWaitTime(m.RDate) * 60 * 60) +
datepart(mi, dbo.fnGetMonthlyAverageWaitTime(m.RDate) * 60) +
datepart(s, dbo.fnGetMonthlyAverageWaitTime(m.RDate)))[MonthlyAverageWaitTime]
from TelephonyMTD m
Error: Operand type clash: time is incompatible with int
So, I tried to run this:
select
(datepart(HH, GetDate() * 60 * 60) +
datepart(mi, GetDate() * 60) +
datepart(s, GetDate()))
Now it says, Implicit conversion from data type datetime to int is not allowed. Use the CONVERT function to run this query. Which is true when I looked at the data type conversion chart, I came to know that conversion to int and float is now allowed.
Please advice.
The problem is you are trying to multiply a date/datetime by an integer which doesn't make sense:
GetDate() * 60 * 60
You could simply use DATEDIFF with seconds, to get the value in seconds:
SELECT DATEDIFF(SECOND, '00:00:00', dbo.fnGetMonthlyAverageWaitTime(m.RDate)) AS MonthlyAverageWaitTime
FROM TelephonyMTD AS m
QUICK EXAMPLE
SELECT t.AvgTime,
AvgTimeInSeconds = DATEDIFF(SECOND, '00:00:00', t.AvgTime)
FROM (VALUES
(CAST('00:01:15' AS TIME)),
(CAST('05:36:47' AS TIME))
) AS t (AvgTime);
Which gives:
+----------+------------------+
| AvgTime | AvgTimeInSeconds |
+----------+------------------+
| 00:01:15 | 75 |
| 05:36:47 | 20207 |
+----------+------------------+
Open and Close brackets are the one causing issue for the above error: Please try like below
SELECT (
( Datepart(HH, Getdate()) * 60 * 60 ) +
( Datepart(mi, Getdate()) * 60 ) +
Datepart(s, Getdate())
)
Try to use Below Syntax to convert seconds to day, hour, minute, seconds
DECLARE #sec INT = 86400;
SELECT
CAST(#sec /60/60/24 AS VARCHAR(12)) + ' Days,'
+ CAST(#sec /60/60 % 24 AS VARCHAR(12))+ ' Hours,'
+ CAST(#sec /60 % 60 AS VARCHAR(2)) + ' Minutes, '
+ CAST(#sec % 60 AS VARCHAR(2)) + ' Seconds.';
I have a table in the following format
reporting_date interest_payment balance
200401 10 10
200402 20 15
200403 30 20
200404 40 30
200405 50 40
200406 60 50
200407 70 60
i wanted to generate an OUTPUT in the following format :
The output of the query should look like this :
reporting_date interest_payment balance
Q1 -2004 60 10
Q2 -2004 150 30
Q3 -2004 70 60
Q4 -2004 0 0
i.e i wanted to represent data by quarter and year and group by quarter and year for interest_payment column but for balance i need to pick up the value from the first reporting date in that quarter ,so as you can see q1-2004 has 10,15 and 20 but only 10 is accounted as that was the first reporting date in that quarter
I have my query working for interest payment but i am not sure how do i pickup the first reporting value for balance in a quarter
SELECT report_year as "#date",'Q'+CAST(report_quarter+1 as varchar(1)) as "#quarter", SUM(a.balance) as "#balance", SUM(a.interest_payment) as "#interest_payment"
FROM (SELECT *,
(reporting_date%100 - 1)/3 as report_quarter,
reporting_date/100 as report_year
FROM employee) a
GROUP by report_year, report_quarter
order by report_year, report_quarter
First, create all combination of years and quarters. Then use window functions such AS SUM OVER() and ROW_NUMBER() for the interest_payment and balance.
CREATE TABLE #temp(
reporting_date INT,
interest_payment INT,
balance INT
)
INSERT INTO #temp VALUES
(200401, 10, 10), (200402, 20, 15), (200403, 30, 20),
(200404, 40, 30), (200405, 50, 40), (200406, 60, 50),
(200407, 70, 60);
;WITH quarters(yr, qtr) AS(
SELECT
y.N, q.N
FROM(
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
) q(N)
CROSS JOIN(
SELECT DISTINCT reporting_date / 100 FROM #temp
) y(N)
)
,GroupedByQtr AS(
SELECT
*,
qtr = (reporting_date % 100 -1) / 3 + 1,
yr = reporting_date / 100,
ss = SUM(interest_payment) OVER(PARTITION BY (reporting_date / 100), ((reporting_date % 100 -1) / 3 + 1)),
rn = ROW_NUMBER() OVER(PARTITION BY (reporting_date / 100), ((reporting_date % 100 -1) / 3 + 1) ORDER BY reporting_date)
FROM #temp
)
SELECT
reporting_date = 'Q' + CONVERT(VARCHAR(1), q.qtr) + ' - ' + CONVERT(VARCHAR(4), q.yr),
interest_payment = ISNULL(g.ss, 0),
balance = ISNULL(g.balance, 0)
FROM quarters q
LEFT JOIN GroupedByQtr g
ON g.qtr = q.qtr
AND g.yr = q.yr
AND g.rn = 1
RESULT
reporting_date interest_payment balance
-------------- ---------------- -----------
Q1 - 2004 60 10
Q2 - 2004 150 30
Q3 - 2004 70 60
Q4 - 2004 0 0