【问题标题】:django postgresql - CASE types interval and integer cannot be matcheddjango postgresql - CASE 类型区间和整数不能匹配
【发布时间】:2019-11-26 22:49:34
【问题描述】:

在 Django 中,我正在创建一个带有 CASE 和注释的查询集。它看起来像这样:

days_diff_calc = Case(
        When(Q(sent_date__isnull=True),
             then=None),
        When(Q(received_date__isnull=True),
             then=(timezone.now().date() - F('sent_date'))),
        default=(F('received_date') - F('sent_date')),
        output_field=DurationField()
    )

Item.objects.all().annotate(
        days_diff=days_diff_calc
    )

它创建的查询的重要部分如下所示:

CASE 
                  WHEN "item"."sent_date" IS NULL THEN NULL 
                  WHEN "item"."received_date" IS NULL THEN ('2019-11-26' - "item"."sent_date") 
                  ELSE (interval '1 day'* ("item"."received_date" - "item"."sent_date"))
END AS "days_diff"  

在 Postgres 中运行时出现此错误:

CASE types interval and integer cannot be matched
LINE 3: ...item"."received_date" IS NULL THEN ('2019-11-2...
                                               ^

似乎出于某种原因,Django ORM 在 ELSE 之后添加了 interval 1 day *,但在第二个 WHEN 之后没有这样做,我认为这就是这个错误的原因。我不明白 - 为什么它只将它添加到一个地方而不是另一个地方,如果我想将它添加到另一个地方(第二个 WHEN) - 我会这样做吗?

编辑:

django 版本 2.1.5, psycopg2 版本 2.6.2, PostgreSQL 9.6

所有“日期”字段的类型均为models.DateField(null=True, blank=True)

也尝试使用 ExpressionWrapper 并得到相同的结果:

days_diff_calc = Case(
        When(Q(sent_date__isnull=True),
             then=None),
        When(Q(received_date__isnull=True),
             then=ExpressionWrapper(timezone.now().date() - F('sent_date'),
                                    output_field=DurationField())),
        default=ExpressionWrapper(F('received_date') - F('sent_date'),
                                  output_field=DurationField()),
    )

【问题讨论】:

  • 我至今无法复制这个。你使用的是什么版本的 django 和 Postgres?另外:你能展示你的项目模型的相关部分吗?同时:您可以尝试将有问题的表达式包装在 ExpressionWrapper 调用中以强制使用 output_field 类型。
  • @RishiG 添加了所需信息。

标签: django postgresql django-annotate


【解决方案1】:

我对这个问题的理解不是很深,但我至少可以提供以下似乎可以解决问题的解决方法:

days_diff_calc = Case(
        When(Q(sent_date__isnull=True), then=None),
        When(Q(received_date__isnull=True),
             then=timedelta(days=1)*(timezone.now().date() - F('sent_date'))),
        default=(F('received_date') - F('sent_date')),
        output_field=DurationField()
    )

WRT 为什么在您的情况下没有发生单位转换,我并不完全清楚。在 Postgres 中,减去日期会产生一个 int,这就是为什么需要将其转换为持续时间的原因。我猜想可能在this line of expressions.py 上,日期文字不被识别为与DateField 相同的类型。如果您在sent_dateDateTimeField 的模型上运行相同的代码,它似乎可以正常工作,但在这种情况下,您不需要额外的区间表达式。

【讨论】:

  • 我按照你的建议做了。导入from datetime import timedelta 并添加timedelta(days=1)*。现在这部分 sql 查询被翻译成 1 day, 0:00:00 * 我在 postgresql 中得到这个错误 - syntax error at or near "day"
  • 很奇怪。那里一定是版本问题。对我来说,它转换为THEN ('1 days 0.000000 seconds'::interval * (...,不过我正在使用 django 2.2.6 和 Postgres 11.2 运行。
猜你喜欢
  • 1970-01-01
  • 2014-03-19
  • 2018-02-05
  • 1970-01-01
  • 1970-01-01
  • 2023-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多