Link to home
Create AccountLog in
Avatar of Tanabe Saori
Tanabe SaoriFlag for Japan

asked on

rounding up and down in mysql query NULLs results

I have to calculate every half hour that a student departs after 15:00.  However, I also have some grace periods: <0:15 = 0 half hour, >16 = 1 half hour.
My query was working until this requirement presented itself. Now HalfHours and Amount are both null:


INSERT INTO `attendence` (`did`, `sid`, `arrived`, `departed`) VALUES (16655, 60, 20, '2023-09-08 09:25:00', '2023-09-08 14:54:00'), (16673, 60, 20, '2023-09-11 09:36:00', '2023-09-11 16:00:00'), (16694, 60, 20, '2023-09-12 09:27:00', '2023-09-12 16:17:00'), (16713, 60, 20, '2023-09-13 09:21:00', '2023-09-13 15:35:00'), (16735, 60, 20, '2023-09-14 09:52:00', '2023-09-14 15:00:00'), (16754, 60, 20, '2023-09-15 09:19:00', '2023-09-15 15:20:00'), (16783, 60, 20, '2023-09-19 09:27:00', '2023-09-19 16:23:00'), (16799, 60, 20, '2023-09-20 09:22:00', '2023-09-20 15:58:00'), (16816, 60, 20, '2023-09-21 09:14:00', '2023-09-21 14:54:00'), (16842, 60, 20, '2023-09-22 09:24:00', '2023-09-22 15:58:00'), (16859, 60, 20, '2023-09-25 09:24:00', '2023-09-25 14:59:00'), (16884, 60, 20, '2023-09-26 09:19:00', '2023-09-26 16:16:00'), (16928, 60, 20, '2023-09-28 09:30:00', '2023-09-28 14:51:00'), (16948, 60, 20, '2023-09-29 09:33:00', '2023-09-29 15:15:00');

Open in new window

Here is my recent query:
SELECT 
            s.sid, 
            CONCAT(s.name, ' ', s.lastname) AS FullName,
            COUNT(*) AS AfterschoolCount,
            GROUP_CONCAT(DATE_FORMAT(a.departed, ' %d|%H:%i') ORDER BY a.departed ASC) AS Dates,
            SUM(
                CASE 
                    WHEN a.departed IS NOT NULL THEN
                        CASE 
                            WHEN MINUTE(a.departed) % 30 >= 16 THEN FLOOR((TIME_TO_SEC(TIMEDIFF(a.departed, '15:00:00')) + 1799) / 1800)
                            WHEN MINUTE(a.departed) % 30 <= 15 THEN CEIL((TIME_TO_SEC(TIMEDIFF(a.departed, '15:00:00')) - 899) / 1800)
                            ELSE FLOOR((TIME_TO_SEC(TIMEDIFF(a.departed, '15:00:00')) + 899) / 1800)
                        END
                    ELSE 0
                END
            ) AS HalfHours,
            SUM(
                CASE 
                    WHEN a.departed IS NOT NULL THEN
                        CASE 
                            WHEN MINUTE(a.departed) % 30 >= 16 THEN FLOOR((TIME_TO_SEC(TIMEDIFF(a.departed, '15:00:00')) + 1799) / 1800) * 500
                            WHEN MINUTE(a.departed) % 30 <= 15 THEN CEIL((TIME_TO_SEC(TIMEDIFF(a.departed, '15:00:00')) - 899) / 1800) * 500
                            ELSE FLOOR((TIME_TO_SEC(TIMEDIFF(a.departed, '15:00:00')) + 899) / 1800) * 500
                        END
                    ELSE 0
                END
            ) AS Amount
        FROM 
            students s
        JOIN 
            attendence a ON s.sid = a.sid
        WHERE 
            TIME(a.departed) > '15:00:00' AND 
            MONTH(a.departed) = 9 AND 
            YEAR(a.arrived) = 2023 AND
            s.active = 'Yes'
        GROUP BY 
            s.sid, FullName
        ORDER BY 
            AfterschoolCount DESC

Open in new window



Avatar of lenamtl
lenamtl
Flag of Canada image


Hi.

I would make the calculation and rounding using server side code this will simplify the SQL query.

So use the SQL get the data from BD and use server side code do the calculation.

ASKER CERTIFIED SOLUTION
Avatar of PortletPaul
PortletPaul
Flag of Australia image

Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
See answer
Avatar of skullnobrains
skullnobrains

if i get it right, the first 15 minutes are free and between 30 and 45 minutes only counts 1 half hour and so on


why not simply ?


FLOOR( TIME_TO_SEC(TIMEDIFF(a.departed, '15:15:00')) / 1800 )

Open in new window

or simpler ?


ROUND( TIME_TO_SEC(TIMEDIFF(a.departed, '15:00:00')) /1800 )

Open in new window

Avatar of Tanabe Saori

ASKER

Thanks so much PortletPaul, the first one seems to be working well.  But, I'm not sure it's doing the < 15, >16 rounding.  For example, looking at sid 127, it looks like it would be 4 half hours, not 5.


The 2nd one you provided doesn't seem to work as well.  Maybe it's my limited understanding.

I think I did it with this:


SUM(
    CASE 
        WHEN a.departed IS NOT NULL THEN
            CASE 
                WHEN HOUR(a.departed) > 15 OR (HOUR(a.departed) = 15 AND MINUTE(a.departed) >= 16) THEN 
                    -- Round up, considering the minute
                    CEIL((TIME_TO_SEC(a.departed) - TIME_TO_SEC('15:00:00')) / 1800)
                WHEN HOUR(a.departed) = 15 AND MINUTE(a.departed) < 16 THEN
                    -- Round down, considering the minute
                    FLOOR((TIME_TO_SEC(a.departed) - TIME_TO_SEC('15:00:00')) / 1800)
                ELSE 0
            END
        ELSE 0
    END
) AS HalfHours

Open in new window



seems overcomplicated.

once you expressed this in halve hours, rounding does the same. the rounding issue will last one second rather than one minute. you can ignore it or just use timediff -1. exit times before 15:30 need to be filtered out.

What data type have you used for columns `arrived`, `departed` ?


I used timestamp in my example, and in v8 of MySQL if I run 

   TIMEDIFF(a.departed, '15:00:00')

I get NULL


But, If I run

   TIMEDIFF(DATE_FORMAT(a.departed, '%H:%m:%s'), '15:00:00')

then I get a non-null result


In the original question, as the calculated columns were NULL, it seemed TIMEDIFF was the cause of the NULLs, and this is why I recommended EXTRACT

https://dbfiddle.uk/gT8dZw-u


didsidarriveddepartedROUND( TIME_TO_SEC(TIMEDIFF(time(a.departed), '15:00:00')) /1800 )
1665560202023-09-08 09:25:002023-09-08 14:54:000
1667360202023-09-11 09:36:002023-09-11 16:00:002
1669460202023-09-12 09:27:002023-09-12 16:17:003
1671360202023-09-13 09:21:002023-09-13 15:35:001
1673560202023-09-14 09:52:002023-09-14 15:00:000
1675460202023-09-15 09:19:002023-09-15 15:20:001
1678360202023-09-19 09:27:002023-09-19 16:23:003
1679960202023-09-20 09:22:002023-09-20 15:58:002
1681660202023-09-21 09:14:002023-09-21 14:54:000
1684260202023-09-22 09:24:002023-09-22 15:58:002
1685960202023-09-25 09:24:002023-09-25 14:59:000
1688460202023-09-26 09:19:002023-09-26 16:16:003
1692860202023-09-28 09:30:002023-09-28 14:51:000
1694860202023-09-29 09:33:002023-09-29 15:15:001
1694860202023-09-29 09:33:002023-09-29 15:14:590


slight correction since i thought your departed date was a time rather than a date.

i used your own query for reference which produced the same issue you had in the first place.

thanks the fiddle for helping me notice.


nb : my query will happily report minus something if the student left early. depending on the use case, that could matter


--


if you know the date of the exam (replace YYYY-MM-DD in the below query).


round(TIMESTAMPDIFF(SECOND, 'YYYY-MM-DD 15:00:00', a.departed) / 1800 )


I agree on the 'overcomplicated', but it does give the results I was expecting.

Thanks for the help.