
h2 数据库的 datediff 函数需严格遵循三参数格式(单位、起始时间、结束时间),而 spock 测试中因 h2 语法校验更严格,导致原本在 mysql 中可运行的双参数 datediff 报错。
h2 数据库的 datediff 函数需严格遵循三参数格式(单位、起始时间、结束时间),而 spock 测试中因 h2 语法校验更严格,导致原本在 mysql 中可运行的双参数 datediff 报错。
在 Spring Boot + H2 + Spock 的测试环境中,你遇到的 Syntax error in SQL statement 并伴随 [*] 指向 SUM(DATEDIFF(...)) 内部,根本原因在于 H2 对 DATEDIFF 函数的语法要求与 MySQL 不同:H2 要求显式指定时间单位(如 DAY、YEAR 等),而 MySQL 允许省略单位默认为 DAY。
你当前的 SQL:
SELECT C.idCampaign, SUM(DATEDIFF(IFNULL(CPM.actual_end, CURDATE()), CPM.actual_start)) AS daysPaused FROM Campaign C JOIN CampaignPauseManagement CPM ON C.idCampaign = CPM.idCampaign WHERE C.idCampaign IN (:campaignIds) GROUP BY C.idCampaign;
在 H2 中会报错,因为 DATEDIFF(a, b) 是非法的——H2 期望的是 DATEDIFF(unit, start, end)。
✅ 正确写法(适配 H2):
SELECT C.idCampaign, SUM(DATEDIFF(DAY, CPM.actual_start, IFNULL(CPM.actual_end, CURDATE()))) AS daysPaused FROM Campaign C JOIN CampaignPauseManagement CPM ON C.idCampaign = CPM.idCampaign WHERE C.idCampaign IN (:campaignIds) GROUP BY C.idCampaign;
⚠️ 注意事项:
- DATEDIFF(DAY, start, end) 返回整数天数差(end - start),结果为正表示 end 在 start 之后;
- IFNULL(CPM.actual_end, CURDATE()) 保持原逻辑:若暂停未结束,则按当前日期计算;
- 单位关键字(如 DAY)不加引号,且必须大写(H2 严格区分大小写);
- 避免混用数据库方言:生产环境若用 MySQL,测试用 H2 时应统一使用 H2 兼容语法,或通过 @SqlConfig(database = Database.H2) 等方式隔离方言。
? 扩展建议:
若需跨数据库兼容,可考虑改用标准 SQL 表达式(如 DATEDIFF 替换为 CEILING((end - start) / (1000 * 60 * 60 * 24))),但更推荐在测试中明确适配 H2,并在集成测试阶段验证真实数据库行为。
总结:H2 的 DATEDIFF 是强类型三元函数,务必传入 unit 参数;修复后不仅 Spock 测试可通过,也提升了 SQL 的可移植性与语义清晰度。

















