You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django ORM与原生SQL查询的记录计数不一致问题

问题:Django ORM与原生SQL查询计数及结果不匹配

核心差异

Django ORM查询返回记录计数为 6529,但直接执行原生SQL查询得到的计数为 6540,同时两者返回的时间字段值存在明显偏移。

Django视图查询代码

start_date = datetime(2024, 10, 19, 0, 0, 0)
end_date = datetime(2024, 10, 19, 23, 59, 59)
dbug = Reservations.objects.all().filter(updated_at__range=(start_date, end_date))
print(dbug.count())

Django配置(settings.py)

TIME_ZONE = 'Asia/Tehran'

USE_TZ = False

原生SQL查询及结果

SELECT COUNT(*) FROM "consultant_reservations"
WHERE updated_at BETWEEN '2024-10-19 00:00:00' AND '2024-10-19 23:59:59';

返回结果:

count 
-------
  6540
(1 row)

具体查询结果对比

原生SQL查询示例

SELECT * FROM "consultant_reservations"
WHERE updated_at BETWEEN '2024-10-19 00:00:00' AND '2024-10-19 23:59:59' LIMIT 4;

返回结果片段(注意updated_at为UTC时间,带+00后缀):

id   |   idd   |       updated_at       
-------+---------+------------------------
 76407 | 2011050 | 2024-10-19 20:28:01+00
  1408 | 1958653 | 2024-10-19 06:28:01+00
  1514 | 1958759 | 2024-10-19 10:28:01+00
 60371 | 1997347 | 2024-10-19 13:28:02+00

Django ORM查询示例

start_date = datetime(2024, 10, 19, 0, 0, 0)
end_date = datetime(2024, 10, 19, 23, 59, 59)
dbug = Reservations.objects.all().filter(updated_at__range=(start_date, end_date))[:4]
data = list(dbug.values())
df = pd.DataFrame(data)
print(df.head(4))

返回结果片段(updated_at为Asia/Tehran时区时间):

id      idd          updated_at
0  76407  2011050 2024-10-19 23:58:01
1   1408  1958653 2024-10-19 09:58:01
2   1514  1958759 2024-10-19 13:58:01
3  60371  1997347 2024-10-19 16:58:02

原因分析

从时间字段的偏移可以看出:

  1. 数据库中updated_at存储的是UTC时间(带+00后缀)。
  2. 由于USE_TZ = False,Django会将数据库的UTC时间转换为TIME_ZONE设置的Asia/Tehran时间(UTC+3:30)返回给应用,但在构造查询条件时,直接使用本地时间(未时区感知的datetime对象)去过滤数据库中的UTC时间,导致查询范围不匹配:
    • 原生SQL查询的是UTC时间的2024-10-19全天。
    • ORM的查询条件是Asia/Tehran时间的2024-10-19全天,对应UTC时间范围为2024-10-18 20:30:00到2024-10-19 20:29:59,因此漏掉了UTC时间在2024-10-19 20:30:00到23:59:59之间的记录,这就是计数差异的根源。

解决方案

方案1:启用时区支持(推荐)

修改settings.py:

USE_TZ = True

然后使用时区感知的datetime对象构造查询条件:

from django.utils import timezone

start_date = timezone.make_aware(datetime(2024, 10, 19, 0, 0, 0))
end_date = timezone.make_aware(datetime(2024, 10, 19, 23, 59, 59))
dbug = Reservations.objects.filter(updated_at__range=(start_date, end_date))

此时Django会自动将时区时间转换为UTC时间去数据库查询,与原生SQL的范围一致。

方案2:手动转换查询条件为UTC时间

如果保持USE_TZ = False,需要将Asia/Tehran时间的范围转换为UTC时间:

import pytz

tehran_tz = pytz.timezone('Asia/Tehran')
start_date_tehran = tehran_tz.localize(datetime(2024, 10, 19, 0, 0, 0))
end_date_tehran = tehran_tz.localize(datetime(2024, 10, 19, 23, 59, 59))

# 转换为UTC时间
start_date_utc = start_date_tehran.astimezone(pytz.utc).replace(tzinfo=None)
end_date_utc = end_date_tehran.astimezone(pytz.utc).replace(tzinfo=None)

dbug = Reservations.objects.filter(updated_at__range=(start_date_utc, end_date_utc))

内容的提问来源于stack exchange,提问作者parmer_110

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 02:20:54