Django 3.1-注释,ExpressionWrapper和分割-SQL错误

问题描述

我正在建立一个查询,该查询会根据频率字段进行过滤。现在,频率不是模型字段,而是从持续时间和计数得出的带注释的值。这是基于frequency = duration / number of times stuff happened这个非常基本的想法。

(非常)简化的模型如下:

import uuid

class Actor(AbstractBaseUser):
    id = models.UUIDField(_('UUID'),default=uuid.uuid4,primary_key=True)

class Event(models.Model):
    actor = models.ForeignKey(Actor,null=True,blank=True,on_delete=models.CASCADE)
    foo = models.CharField(_('Foo'),max_length=10,null=True)
    bar = models.CharField(_('Bar'),null=True)
    when = models.DateTimeField(_('When'),blank=True)

查询背后的想法是:

  1. 每个演员都带有 event_count 注释:它是父对象的Event对象的数量
  2. 每个演员的注释都带有 first_event_datetime :其最早的子事件对象的日期时间
  3. 每个演员都用 duration_over 进行注释:Now()(数据库函数)与first_event_datetime之间的持续时间
  4. 在考虑的时间段 event_frequency 中,每个演员都以事件的“ event_frequency”进行注释:duration_over / event_count

请记住,查询的构建方式如下:

from django.db import models
from django.db.models import Q,Sum,Avg,F,Count,OuterRef,Subquery,Exists,Value,ExpressionWrapper
from django.db.models.functions import Coalesce,Now,Mod,Cast

actors = Actor.objects.annotate(
    event_count=Coalesce(Subquery(
        Event.objects.filter(
            actor__pk=OuterRef('pk')
        ).values(
            'actor__pk'
        ).order_by(
            'actor__pk'
        ).annotate(
            count=Count('pk',distinct=True)
        ).values(
            'count'
        ),output_field=models.IntegerField()
    ),0)
).annotate(
    first_event_datetime=Subquery(
        Event.objects.filter(
            actor__pk=OuterRef('pk')
        ).order_by(
            'when'
        ).values(
            'when'
        )[:1],output_field=models.DateTimeField()
    )
).annotate(
    duration_over=ExpressionWrapper(
        Now() - F('first_event_datetime'),output_field=models.DurationField()
    )
).annotate(
    event_frequency=ExpressionWrapper(
        F('duration_over') / F('transaction_count'),output_field=models.DurationField()
    )
).filter(
    # might want to avoid db-side divide by zero? :-/
    event_count__gte=1,# or any other timedelta
    event_frequency__gt=timedelta(days=2)
)

结果如下:

    Traceback (most recent call last):
  File "<...>main/env/lib/python3.6/site-packages/django/db/backends/utils.py",line 84,in _execute
    return self.cursor.execute(sql,params)
  File "<...>main/env/lib/python3.6/site-packages/django/db/backends/MysqL/base.py",line 73,in execute
    return self.cursor.execute(query,args)
  File "<...>main/env/lib/python3.6/site-packages/MysqLdb/cursors.py",line 209,in execute
    res = self._query(query)
  File "<...>main/env/lib/python3.6/site-packages/MysqLdb/cursors.py",line 315,in _query
    db.query(q)
  File "<...>main/env/lib/python3.6/site-packages/MysqLdb/connections.py",line 239,in query
    _MysqL.connection.query(self,query)
MysqLdb._exceptions.ProgrammingError: (1064,"You have an error in your sql Syntax; check the manual that corresponds to your MysqL server version for the right Syntax to use near '/ COALESCE((SELECT COUNT(disTINCT U0.`id`) AS `count` FROM `event_event' at line 1")

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<console>",line 1,in <module>
  File "<...>main/env/lib/python3.6/site-packages/django/db/models/query.py",line 263,in __repr__
    data = list(self[:REPR_OUTPUT_SIZE + 1])
  File "<...>main/env/lib/python3.6/site-packages/django/db/models/query.py",line 287,in __iter__
    self._fetch_all()
  File "<...>main/env/lib/python3.6/site-packages/django/db/models/query.py",line 1308,in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "<...>main/env/lib/python3.6/site-packages/django/db/models/query.py",line 53,in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch,chunk_size=self.chunk_size)
  File "<...>main/env/lib/python3.6/site-packages/django/db/models/sql/compiler.py",line 1156,in execute_sql
    cursor.execute(sql,params)
  File "<...>main/env/lib/python3.6/site-packages/django/db/backends/utils.py",line 98,in execute
    return super().execute(sql,line 66,in execute
    return self._execute_with_wrappers(sql,params,many=False,executor=self._execute)
  File "<...>main/env/lib/python3.6/site-packages/django/db/backends/utils.py",line 75,in _execute_with_wrappers
    return executor(sql,many,context)
  File "<...>main/env/lib/python3.6/site-packages/django/db/backends/utils.py",params)
  File "<...>main/env/lib/python3.6/site-packages/django/db/utils.py",line 90,in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "<...>main/env/lib/python3.6/site-packages/django/db/backends/utils.py",query)
django.db.utils.ProgrammingError: (1064,"You have an error in your sql Syntax; check the manual that corresponds to your MysqL server version for the right Syntax to use near '/ COALESCE((SELECT COUNT(disTINCT U0.`id`) AS `count` FROM `event_event' at line 1")

我尝试将event_frequency注释值更改为以下内容

  • Cast(F('duration_over'),models.DurationField()) / Cast(F('event_count'),models.IntegerField())
  • 以及CastExpressionWrapper ...的其他类似组合。

无济于事。

有什么想法吗?

非常感谢!

编辑:略有修改的原始sql如下所示:

SELECT `actor`.`id` 
FROM `actor` 
WHERE (
  COALESCE(
    (
      SELECT COUNT(disTINCT U0.`id`) AS `count` 
      FROM `event` U0 
      WHERE U0.`actor_id` = `actor`.`id` 
      GROUP BY U0.`actor_id` 
      ORDER BY NULL
    ),0
  ) >= 1 
  AND (
    INTERVAL 
      TIMESTAMPDIFF(
        MICROSECOND,(
          SELECT U0.`transaction_datetime` 
          FROM `event` U0 
          WHERE U0.`actor_id` = `actor`.`id` 
          ORDER BY U0.`transaction_datetime` 
          ASC LIMIT 1
        ),CURRENT_TIMESTAMP
      ) MICROSECOND 
      / 
      COALESCE(
        (
          SELECT COUNT(disTINCT U0.`id`) AS `count` 
          FROM `event` U0 
          WHERE U0.`actor_id` = `actor`.`id` 
          GROUP BY U0.`actor_id` 
          ORDER BY NULL
        ),0
      )
  ) > 86400000000
)

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)