Rob*_*jic 7 python django postgresql django-models django-orm
我有一个 PostgreSQLUPDATE查询,它根据同一表中global_ranking按另一个字段 ( ) 排序的每行的字段 ( ) 更新表中每一行的字段 ( )。此外,更新是分区的,因此每行的排名仅相对于属于同一 的那些行。ROW_NUMBER()ratinglanguage
简而言之,我根据每个玩家当前的评分来更新游戏中每个玩家的排名。
PostgreSQL 查询如下所示:
UPDATE stats_userstats
SET
global_ranking = sub.row_number
FROM (
SELECT id, ROW_NUMBER() OVER (
PARTITION BY language
ORDER BY rating DESC
) AS row_number
FROM stats_userstats
) sub
WHERE stats_userstats.id = sub.id;
Run Code Online (Sandbox Code Playgroud)
我也在使用 Django,如果可能的话,学习如何使用 Django ORM 表达此查询会很有趣。
起初,Django 似乎拥有表达查询所需的一切,包括使用 PostgreSQL 的ROW_NUMBER()窗口函数的能力,但我最好的尝试是使用以下命令更新所有ranking行1:
from django.db.models import F, OuterRef, Subquery
from django.db.models.expressions import Window
from django.db.models.functions import RowNumber
UserStats.objects.update(
global_ranking=Subquery(
UserStats.objects.filter(
id=OuterRef('id')
).annotate(
row_number=Window(
expression=RowNumber(),
partition_by=[F('language')],
order_by=F('rating').desc()
)
).values('row_number')
)
)
Run Code Online (Sandbox Code Playgroud)
我曾经from django.db import connection; print(connection.queries)看过 Django ORM 语句生成的查询,并得到了这个模糊相似的 SQL 语句:
UPDATE "stats_userstats"
SET "global_ranking" = (
SELECT ROW_NUMBER() OVER (
PARTITION BY U0."language"
ORDER BY U0."rating" DESC
) AS "row_number"
FROM "stats_userstats" U0
WHERE U0."id" = "stats_userstats"."id"
Run Code Online (Sandbox Code Playgroud)
看起来我需要做的是将子查询从SET查询部分移动到FROM,但我不清楚如何重构 Django ORM 语句来实现这一点。
任何帮助是极大的赞赏。谢谢你!
Subquery按提供的过滤 qs OuterRef。1如果只考虑每个用户,您总是会得到任何排名中的第一名。
一个“正确”的查询是:
UserStats.objects.alias(
row_number=Window(
expression=RowNumber(),
partition_by=[F('language')],
order_by=F('rating').desc()
)
).update(global_ranking=F('row_number'))
Run Code Online (Sandbox Code Playgroud)
但 Django 不允许这样做:
django.core.exceptions.FieldError: Window expressions are not allowed in this query
相关 Django 票证:https://code.djangoproject.com/ticket/25643
我想你可以在那里评论你的用例。