Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby-on-rails-4/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql Django Q查询及;在同一领域?_Sql_Django_Django Queryset_Django Q - Fatal编程技术网

Sql Django Q查询及;在同一领域?

Sql Django Q查询及;在同一领域?,sql,django,django-queryset,django-q,Sql,Django,Django Queryset,Django Q,以下是我的模型: class Event(models.Model): user = models.ForeignKey(User, blank=True, null=True, db_index=True) name = models.CharField(max_length = 200, db_index=True) platform = models.CharField(choices = (("ios", "ios"), ("android", "android"

以下是我的模型:

class Event(models.Model):
    user = models.ForeignKey(User, blank=True, null=True, db_index=True)
    name = models.CharField(max_length = 200, db_index=True)
    platform = models.CharField(choices = (("ios", "ios"), ("android", "android")), max_length=50)

class User(AbstractUser):
    email = models.CharField(max_length=50, null=False, blank=False, unique=True)
Event
类似于一个分析事件,因此如果一个用户登录多个设备,我很可能会为一个用户创建多个事件,一些事件使用
platform=ios
,另一些事件使用
platform=android
。我想查询一下有多少用户同时拥有ios和android设备。所以我写了一个这样的查询:

User.objects.filter(Q(event__platform="ios") & Q(event__platform="android")).count()
返回0个结果。我知道这是不对的。然后我想我会尝试只查询iOS用户:

User.objects.filter(Q(event__platform="ios")).count()

它返回了6717622个结果,这是出乎意料的,因为我只有39294个用户。我猜这不是计算用户,而是计算
事件
实例,这在我看来似乎是不正确的行为。有人对此问题有什么见解吗?

您可以使用注释:

django.db.models import Count

User.objects.all().annotate(events_count=Count('event')).filter(events_count=2)
因此,它将过滤掉具有两个事件的任何用户

还可以使用链接过滤器:

User.objects.filter(event__platform='android').filter(event__platform='ios')

第一个过滤器将获得所有使用android平台的用户,第二个过滤器将获得同样使用iOS平台的用户。

这通常是对具有两个或更多与子对象相关条件的查询集的回答

解决方案:即使没有任何联接,也可以使用包含两个子查询的简单解决方案:

base\u subq=Event.objects.values('user\u id').order\u by().distinct()
user_qs=user.objects.filter(
Q(pk\uu in=base\u subq.filter(platform=“android”))&
Q(pk\uu in=base\u subq.filter(platform=“ios”))
)
如果模型事件具有默认顺序,则方法
.order\u by()
非常重要(请参见文档中关于distinct()方法的部分)


注释

验证将执行的唯一SQL请求:(通过删除“app_”前缀简化。)

  • 之所以使用函数
    Q()
    ,是因为相同的条件参数(
    pk\u in
    )不能在相同的
    filter()
    中重复,但也可以使用链式过滤器:
    .filter(…).filter(…)
    。(筛选条件的顺序并不重要,SQL server optimizer估计的首选项会超过它。)
  • 临时变量
    base\u subq
    是一个“别名”查询集,仅用于不重复从未单独计算过的表达式的同一部分
  • 用户(父对象)和事件(子对象)之间的一个连接不会是问题,也可以使用一个子查询解决问题,但无论如何,子查询都应该避免使用事件和事件的连接(使用重复的子对象或两个子对象的连接)。两个子查询对于可读性很好,可以演示两个过滤条件的对称性

另一个包含两个嵌套子查询的解决方案如果我们知道一个子查询(我们放在最里面)的过滤器比另一个包含大量结果的必要子查询的过滤器要严格得多,那么这个非对称解决方案可能会更快。(例如,如果Android用户数量庞大)

ios\u user\u id=(Event.objects.filter(platform=“ios”)
.values('user_id')。order_by().distinct())
user\u id=(Event.objects.filter(platform=“android”,user\u id\u in=ios\u user\u id)
.values('user_id')。order_by().distinct())
user\u qs=user.objects.filter(pk\u in=user\u id)
验证它是如何编译为SQL的:(通过删除
app\uu
前缀和
再次简化)



(这些解决方案也适用于旧的Django,例如1.8。自Django 1.11以来,对于更复杂的情况,存在一个特殊的子查询函数,但对于这个简单的问题,我们不需要它。)

第一个查询注释了每个用户的计数。第二个很好,为我之前的评论表示歉意。它似乎与询问者的第一个查询相同,但不是在不过滤x-to-many关系的情况下进行的。我不应该挑剔,但我并不特别喜欢这个答案。调用
user.objects.filter(event\u platform='android'))
导致一个连接并返回>6M个结果,而我的问题是为什么我不能得到39k个结果。我认为答案是我需要使用
.distinct()
,这似乎与文档所说的相矛盾。然后将其与另一个
.filter()链接
将导致另一个连接,当您有一个大于6M行的表时,该连接不会很快返回。您将根据另一个表筛选结果,以便ofc您将有连接。这是您设计数据库的方式,也是数据库的工作方式。您可以获取android和ios的ID,并使用它们,这比筛选更快通过字符串。您还可以对某些差异查询集进行第二次筛选,速度更快。如何对此类查询进行优化超出了这个问题的范围,您可以打开另一个问题并开始讨论,但这是使用django orm对您的问题的答案,除非您想使用原始sql,这仍然是另一个主题输出sql。第二个查询看起来很好,请尝试在计数之前添加
.order\u by()
,以删除任何默认顺序,然后查看它是否有效。模型的
中定义的默认顺序可能会以微妙的方式破坏您。我添加了一个
.order\u by('user\u id')
,结果相同。有效的是添加
.distinct('id'))
,尽管查询仍然需要很长时间。我的猜测是,如果不扁平化我的数据库结构,我无法更快地完成查询。我假设您已经在
平台上有了一个
。使用整数而不是Navid建议的字符串也会有所帮助。最后,使用原始SQL,它不使用联接,而是访问
事件ble只能将您的查询速度提高两个数量级(但对于这种大小的表仍然不能立即生成结果)。2个数量级应该是完美的。
>>> print(str(user_qs.query))
SELECT user.id, user.email FROM user WHERE (
    user.id IN (SELECT DISTINCT U0.user_id FROM event U0 WHERE U0.platform = 'android')
    AND
    user.id IN (SELECT DISTINCT U0.user_id FROM event U0 WHERE U0.platform = 'ios')
)
>>> print(str(user_qs.query))
SELECT user.id, user.email FROM user 
WHERE user.id IN (
    SELECT DISTINCT V0.user_id FROM event V0
    WHERE V0.platform = 'ios' AND V0.user_id IN (
        SELECT DISTINCT U0.user_id FROM event U0
        WHERE U0.platform = 'android'
    )
)