DateTime范围查询偏差一天无结果的问题解决咨询
问题场景
我有一张包含时间戳的表,数据如下:
| DateAdded |
|---|
| 2023-07-14 08:15:38.1487803 |
| 2023-07-14 08:13:31.4134359 |
| 2023-07-14 08:13:31.4134358 |
模型中该字段定义为:
DateTime DateAdded {get; set; }
我要查询7月14日添加的所有记录,用了以下代码:
DateTime from = new DateTime(2023, 7, 14); DateTime to = from.AddDays(1); DbSet<Items> dbSet = ...; IList<Items> result = await dbSet.Where(x => x.DateAdded >= from && x.DateAdded <= to).ToListAsync();
但查询返回0条数据,生成的SQL语句是:
WHERE DateAdded >= '2023-07-14T00:00:00.000000' AND DateAdded <= '2023-07-15T00:00:00.000000'
我推测是字符串比较时,SQL里的"T"在表中数据的空格之后,导致无法匹配。虽然调整时间范围为:
DateTime from = new DateTime(2023, 7, 14).AddDays(-1); DateTime to = from;
生成的SQL:
WHERE DateAdded >= '2023-07-13T00:00:00.000000' AND DateAdded <= '2023-07-14T00:00:00'
能得到结果,但逻辑明显错误。请问如何修改表结构或查询语句以正确返回目标数据?
核心原因
你的推测有误,问题本质是数据库中的DateAdded字段实际存储为字符串类型(比如varchar/nvarchar),而非日期时间类型。如果是日期类型,数据库会按时间值比较,完全不会在乎格式里的空格或"T"。之所以临时方法能得到结果,是因为字符串按字符顺序比较时,空格的ASCII码小于"T",所以2023-07-14 08:...会被判定为小于2023-07-14T00:...,刚好被<= '2023-07-14T00:...'包含,但这完全是巧合,逻辑错误。
解决方案
方案1:修改表结构(推荐)
把数据库中DateAdded字段的类型从字符串改成对应数据库的日期时间类型:
- SQL Server用
datetime2(7)(刚好匹配7位小数时间戳) - MySQL用
DATETIME(7) - PostgreSQL用
TIMESTAMP(7)
修改后,模型中的DateTime DateAdded无需改动,EF会自动正确映射。此时你最初的查询逻辑(建议把<=改成<,避免包含下一天的0点记录)就能正常工作,数据库会按时间值而非字符串比较。
方案2:临时修改查询语句(仅用于无法改表的场景)
如果暂时不能修改表结构,有两种方式调整查询:
方式A:将字符串转成日期类型后比较
以SQL Server为例,用CONVERT函数转换格式后比较,EF Core可通过原生SQL实现:
DateTime from = new DateTime(2023, 7, 14); DateTime to = from.AddDays(1); var result = await dbSet .FromSqlRaw(@"SELECT * FROM Items WHERE CONVERT(datetime2, DateAdded, 121) >= @from AND CONVERT(datetime2, DateAdded, 121) < @to", new SqlParameter("@from", from), new SqlParameter("@to", to)) .ToListAsync();
注:121是SQL Server对应yyyy-MM-dd HH:mm:ss.fff格式的转换代码,适配你的时间戳格式。
方式B:构造匹配格式的字符串范围
因为时间戳是yyyy-MM-dd HH:mm:ss.fffffff格式,直接构造对应的字符串起始和结束值:
string fromStr = "2023-07-14 00:00:00.0000000"; string toStr = "2023-07-15 00:00:00.0000000"; var result = await dbSet .Where(x => x.DateAdded >= fromStr && x.DateAdded < toStr) .ToListAsync();
这里用< toStr而非<=,避免误包含下一天的0点记录。
内容的提问来源于stack exchange,提问作者Stefan

