Вероятно это примерно то же, что показал выше Лентюй, но не думаю что возможно более эффективное по скорости решение
SELECT
timeStart, timeEnd, ROUND(IFNULL(seconds, 0) / 3600 * 100) as percent
FROM
(SELECT
timeStart,
timeEnd,
SUM(3600 - CASE
WHEN UNIX_TIMESTAMP(timeStarted) - UNIX_TIMESTAMP(timeStart) < 0 THEN 0
ELSE UNIX_TIMESTAMP(timeStarted) - UNIX_TIMESTAMP(timeStart) END - CASE
WHEN UNIX_TIMESTAMP(timeEnd) - UNIX_TIMESTAMP(timeStopped) < 0 THEN 0
ELSE UNIX_TIMESTAMP(timeEnd) - UNIX_TIMESTAMP(timeStopped) END) as seconds
FROM (
SELECT '2020-06-23 00:00:00' as timeStart, '2020-06-23 00:59:59' as timeEnd
UNION SELECT '2020-06-23 01:00:00', '2020-06-23 01:59:59'
UNION SELECT '2020-06-23 02:00:00', '2020-06-23 02:59:59'
UNION SELECT '2020-06-23 03:00:00', '2020-06-23 03:59:59'
UNION SELECT '2020-06-23 04:00:00', '2020-06-23 04:59:59'
UNION SELECT '2020-06-23 05:00:00', '2020-06-23 05:59:59'
UNION SELECT '2020-06-23 06:00:00', '2020-06-23 06:59:59'
// и т.д.
) as HoursSet
LEFT JOIN (
SELECT MAX(IFNULL(StartedPoints.time, '2020-06-23 00:00:00')) as timeStarted, StoppedPoints.time as timeStopped FROM `stanki` as StoppedPoints
LEFT JOIN stanki as StartedPoints
ON StoppedPoints.name = StartedPoints.name AND
StartedPoints.value = 'ACTIVE' AND
StoppedPoints.time > StartedPoints.time
WHERE StoppedPoints.name = '01' AND StoppedPoints.value IN ('STOPPED', 'UNAVAILABLE')
GROUP BY StoppedPoints.time
UNION ALL
SELECT time, '2020-06-24 00:00:00'
FROM (SELECT * FROM stanki WHERE name = '01' ORDER BY time DESC LIMIT 1) as LastRecord
WHERE value = 'ACTIVE'
) as ActiveIntervals ON ActiveIntervals.timeStopped > HoursSet.timeStart
AND ActiveIntervals.timeStarted <= HoursSet.timeEnd
GROUP BY timeStart, timeEnd) as CalculatedSeconds