INSERT INTO `sum` (`year`, `month`, `day`, `hour`, `range_diff`, `avg_oil`, `max_speed`, `avg_speed`)
SELECT
YEAR(r.`send_time`) AS `year`,
MONTH(r.`send_time`) AS `month`,
DAY(r.`send_time`) AS `day`,
HOUR(r.`send_time`) AS `hour`,
MAX(r.`range`) - MIN(r.`range`) AS `range_diff`,
AVG(r.`oil`) AS `avg_oil`,
MAX(g.`speed`) AS `max_speed`,
AVG(g.`speed`) AS `avg_speed`
FROM `realtime` r
INNER JOIN `gps` g ON r.`send_time` = g.`send_time` -- 一一對應,用內連接
GROUP BY `year`, `month`, `day`, `hour`
ON DUPLICATE KEY UPDATE
`range_diff` = VALUES(`range_diff`),
`avg_oil` = VALUES(`avg_oil`),
`max_speed` = VALUES(`max_speed`),
`avg_speed` = VALUES(`avg_speed`);
-- MySQL Event Scheduler (數據庫層定時,無需外部依賴)
SET GLOBAL event_scheduler = ON;
CREATE EVENT `etl_hourly_sum`
ON SCHEDULE EVERY 1 HOUR
STARTS CURRENT_TIMESTAMP + INTERVAL 1 MINUTE -- 錯開整點寫入高峰
DO
INSERT INTO `sum` ... (上述 SQL) ... ;
| 策略 | 說明 |
|---|---|
| 分區表 | `sum` 表按 `RANGE COLUMNS(year, month, day)` 分區,歷史分區歸檔/刪除快。 |
| 增量抽取 | WHERE `send_time` > 上次處理最大時間,避免全表掃描。 |
| 索引優化 | 源表 `send_time` 建索引,目標表組合主鍵即索引。 |
| 批量提交 | 應用層分批次 (如每 1 萬行) 執行 INSERT,控制事務大小。 |
String sql = "INSERT INTO `sum` ... SELECT ... FROM `realtime` r JOIN `gps` g ON r.send_time = g.send_time "
+ "WHERE r.send_time > ? AND r.send_time <= ? " // 增量時間窗
+ "GROUP BY YEAR(r.send_time), MONTH(r.send_time), DAY(r.send_time), HOUR(r.send_time) "
+ "ON DUPLICATE KEY UPDATE ...";
try (Connection conn = ds.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setTimestamp(1, lastProcessedTime);
ps.setTimestamp(2, currentWindowEnd);
ps.executeUpdate();
// 更新 lastProcessedTime
}
**「單條 SQL 完成 JOIN、分組聚合、Upsert 寫入」** 是 MySQL 處理此類定時彙總任務的最高效模式。利用 `ON DUPLICATE KEY UPDATE` 實現冪等寫入,配合事件調度器或應用層定時任務,構建穩健、高性能的數據彙總管道。