【问题标题】:Avoiding cartesian product with Django使用 Django 避免笛卡尔积
【发布时间】:2023-03-06 00:17:02
【问题描述】:

我定义了以下三个表。

class Operator(models.Model):
    DisplayName = models.CharField(max_length=64)

    class Meta:
        app_label = "Experiment"
        db_table = "EXPERIMENT_OPERATOR"

class OperatorSummary(models.Model):
    Operator = models.ForeignKey(Operator, related_name="TransactionSummary")
    TransactionCount = models.IntegerField()
    TransactionValue = models.DecimalField(max_digits=18, decimal_places=2)
    StartTime = models.DateTimeField(default=timezone.now())

    class Meta:
        app_label = "Experiment"
        db_table = "EXPERIMENT_OPERATORSUMMARY"

class OperatorAlerts(models.Model):
    Operator = models.ForeignKey(Operator, related_name="AlertSummary")
    AlertScore = models.IntegerField()
    AlertCount = models.IntegerField()
    StartTime = models.DateTimeField(default=timezone.now())

    class Meta:
        app_label = "Experiment"
        db_table = "EXPERIMENT_OPERATORALERTS"

对于Operator,我想检索给定日期范围内的AlertScoreTransactionCount。我正在使用的查询如下所示:

tz = timezone.get_default_timezone()    
vs = Operator.objects.filter(DisplayName="Jimmy",
                             TransactionSummary__StartTime__gte=tz.localize(datetime(year=2013, month=10, day=1)),
                             AlertSummary__StartTime__gte=tz.localize(datetime(year=2013, month=10, day=1)))\
    .annotate(TotalTransactions=Sum("TransactionSummary__TransactionCount"),
              TotalAlerts=Sum("AlertSummary__AlertScore"))\
    .values("DisplayName", "TransactionSummary__TransactionCount", "AlertSummary__AlertScore")

此查询执行笛卡尔积并返回OperatorAlertsOperatorSummary 表中与查询匹配的所有行。这是它返回的内容:

{'AlertSummary__AlertScore': 20, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 10}
{'AlertSummary__AlertScore': 44, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 10}
{'AlertSummary__AlertScore': 543, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 10}
{'AlertSummary__AlertScore': 20, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 22}
{'AlertSummary__AlertScore': 44, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 22}
{'AlertSummary__AlertScore': 543, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 22}
{'AlertSummary__AlertScore': 20, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 234}
{'AlertSummary__AlertScore': 44, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 234}
{'AlertSummary__AlertScore': 543, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 234}

我想解决这个问题,以便得到以下结果:

{'AlertSummary__AlertScore': 607, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 266}

所有结果都折叠成一行,其中 AlertScoreTransactionCount 相加。

这可能吗?我总是可以回退到对OperatorAlertsOperatorSummary 进行单独查询,然后在Python 中迭代结果集以获得我想要的结果或调用.aggregate,但我确信一定有更好的方式?

【问题讨论】:

    标签: python sql django cartesian-product cross-join


    【解决方案1】:

    尝试颠倒应用values()annotate() 方法的顺序。 values() 应该是第一位的:

    vs = Operator.objects.filter(DisplayName="Jimmy",
                                 TransactionSummary__StartTime__gte=tz.localize(datetime(year=2013, month=10, day=1)),
                                 AlertSummary__StartTime__gte=tz.localize(datetime(year=2013, month=10, day=1)))\
        .values("DisplayName")\
        .annotate(TotalTransactions=Sum("TransactionSummary__TransactionCount"),
                  TotalAlerts=Sum("AlertSummary__AlertScore"))
    

    这将按values() 中提到的字段对结果进行分组,然后为每个组生成一个注释。订单非常重要 - as documented

    以您的方式应用values()annotate()(即annotate()values() 之前)将为每个项目分别生成注释。

    请注意,上面的代码将按DisplayName 对结果进行分组。您可能希望按不同的字段进行分组,例如 pk

    另外,我假设在您的实际代码中,您将希望一次获取多个运算符的值。如果您总是一次查询一个运算符(就像您在示例中所做的那样),您最好使用 aggregate() 而不是 annotate()

    【讨论】:

    • 感谢您提供的非常有帮助的答案。你是对的,我想一次查询多个运算符。但是,我仍然需要 annotate,因为我希望 TotalTransactionsTotalAlerts 用于每个用户,而不是所有用户的聚合。
    • 我同意,这正是我的意思。我认为这句话有点混乱,因为我使用了错误的语法形式。我现在更正了。
    猜你喜欢
    • 1970-01-01
    • 2019-10-20
    • 2017-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-30
    相关资源
    最近更新 更多