如何通过SQL脚本对VARCHAR类型的日期时间进行升序排序?
解决VARCHAR类型日期时间列的完整升序排序问题
你的问题根源在于把日期时间以字符串格式(hh:mm:ss dd-MM-yyyy)存在VARCHAR列中,字符串排序是按字典序进行的,而这个格式的字符串顺序和实际的日期时间逻辑完全不匹配:
- 排序时会优先比较时间部分(比如"01:00:00"会比"02:00:00"先出现),完全忽略后面的日期信息
- 即使看日期部分,
dd-MM-yyyy的格式也会导致字典序混乱(比如"10-01-2023"会比"01-12-2023"靠后,因为字符串"10"比"01"大)
下面给你三种解决方案,按推荐程度排序:
1. 最优方案:改用日期时间类型存储(强烈推荐)
这是从根源解决问题的方式,既避免排序错误,又能保证数据合法性,还能提升存储和查询效率。
步骤:
- 修改表结构:把
Timedone列的类型从VARCHAR(255)改成DATETIME2(SQL Server推荐的高精度日期时间类型):ALTER TABLE [devnew].[dbo].[Table1] ALTER COLUMN Timedone DATETIME2; - 调整Groovy代码:不需要把
LocalDateTime转成字符串,直接存储日期时间对象:
如果你的实体类字段是def date = Finish / 1000; LocalDateTime dateTime = LocalDateTime.ofEpochSecond(date, 0, ZoneOffset.UTC); // 直接设置LocalDateTime类型字段,ORM框架会自动处理JDBC类型转换 main.setdoneTime(dateTime);java.sql.Timestamp,可以做简单转换:Timestamp timestamp = Timestamp.valueOf(dateTime); main.setdoneTime(timestamp); - 排序SQL:直接按列排序即可,完全符合日期时间逻辑:
select * from [devnew].[dbo].[Table1] WHERE Total!= Right + Left order by Timedone
2. 不改表结构:排序时将字符串转成日期时间类型
如果暂时无法修改表结构,可以在排序阶段把字符串解析成日期时间类型后再排序,适用于SQL Server的写法如下:
方法A:使用TRY_CONVERT函数(兼容格式错误的数据)
你的字符串格式是hh:mm:ss dd-MM-yyyy,可以用TRY_CONVERT指定格式代码解析:
select * from [devnew].[dbo].[Table1] WHERE Total!= Right + Left order by TRY_CONVERT(DATETIME2, Timedone, 131)
格式代码131对应SQL Server的
dd/mm/yyyy hh:mi:ss:mmm(24h),如果你的时间是12小时制(hh)且包含AM/PM标识,解析会更准确;如果没有,建议后续把Groovy的格式改成24小时制的HH:mm:ss。
方法B:使用TRY_PARSE函数(更灵活的格式解析)
TRY_PARSE可以指定区域来解析日期字符串,针对dd-MM-yyyy的格式,用英式英语区域即可:
select * from [devnew].[dbo].[Table1] WHERE Total!= Right + Left order by TRY_PARSE(Timedone AS DATETIME2 USING 'en-GB')
TRY_PARSE解析失败会返回NULL,这些值会排在结果集的最前或最后(取决于SQL Server的排序设置);如果用PARSE,解析失败会直接报错,适合数据格式完全规范的场景。
3. 临时方案:修改字符串格式为可排序的格式
如果既不能改表结构,也不想在SQL里做转换,可以调整Groovy的日期格式,让字符串的字典序和日期时间顺序一致:
def date = Finish / 1000; LocalDateTime dateTime = LocalDateTime.ofEpochSecond(date, 0, ZoneOffset.UTC); // 改用yyyy-MM-dd hh:mm:ss格式,字符串排序时会按年、月、日、时间的逻辑排序 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss", Locale.ENGLISH); String formattedDate = dateTime.format(formatter); main.setdoneTime(formattedDate);
之后原有的排序SQL就可以正常工作了,不过这种方法只是规避了问题,还是不如用日期时间类型存储可靠。
内容的提问来源于stack exchange,提问作者user9630935




