在过滤查询集上使用窗口函数遇到了一个令人惊讶的难题。
考虑两个模型:mymodel和relatedmodel,它们之间存在一对多的关系(即relatedmodel在mymodel中包含一个ForeignKey)。
我正在使用类似这样的东西:
window_lag = Window(expression=Lag("pk"), order_by=order_by)
window_lead = Window(expression=Lead("pk"), order_by=order_by)
window_rownnum = Window(expression=RowNumber(), order_by=order_by)
qs1 = mymodel.objects.filter(relatedmodel__field=XXX)
qs2 = qs1.annotate(row=window_rownnum, prior=window_lag, next=window_lead)
qs3 = qs2.filter(pk=myid)
它为对象pk = myid返回一个漂亮的结果,现在它位于过滤列表及其上一个和下一个位置,在浏览过滤列表中,我用它发挥了很大的作用。
显然len(qs1)= len(qs2)是列表的大小,len(qs3)= 1
A,我刚刚发现过滤条件不太明确时,此中断很严重:
window_lag = Window(expression=Lag("pk"), order_by=order_by)
window_lead = Window(expression=Lead("pk"), order_by=order_by)
window_rownnum = Window(expression=RowNumber(), order_by=order_by)
qs1 = mymodel.objects.filter(relatedmodel__field__contains=X)
qs2 = qs1.annotate(row=window_rownnum, prior=window_lag, next=window_lead)
qs3 = qs2.filter(pk=myid)
在这种情况下,qs2突然比qs1包含更多行!还有len(qs2)> len(qs1)。
从某种意义上说,这完全破坏了浏览器(因为前一个和下一个不再可靠)。多余的行是重复的mymodel对象,只要不止一个相关的model对象符合条件。
我已将其追溯到生成的SQL。
这是qs1的SQL形式:
SELECT DISTINCT
"mymodel"."id", "mymodel"."order_by" ....
FROM "mymodel"
INNER JOIN "relatedmodel" ON ("mymodel"."id" = "relatedmodel"."mymodel_id")
WHERE ("related_model"."field"::text LIKE '%X%')
ORDER BY "mymodel"."order_by" ASC
这在我的数据库引擎中可以很好地作为SQL查询运行,并返回与Django所见相同的行数。一切都很好。
然后qs2生成的SQL类似于:
SELECT DISTINCT
ROW_NUMBER() OVER (ORDER BY "mymodel"."order_by" ASC) AS "row",
LAG("mymodel"."id", 1) OVER (ORDER BY "mymodel"."order_by" ASC) AS "prior,
LEAD("mymodel"."id", 1) OVER (ORDER BY "mymodel"."order_by" ASC) AS "next",
"mymodel"."id", "mymodel"."order_by" ....
FROM "mymodel"
INNER JOIN "relatedmodel" ON ("mymodel"."id" = "relatedmodel"."mymodel_id")
WHERE ("related_model"."field"::text LIKE '%X%')
ORDER BY "mymodel"."order_by" ASC
这又产生了与我在Django中看到的相同数量的行,但是当relatedmodel多次匹配时,它的行数大于qs1。
我可以对SQL进行诊断并获得所需的内容,即通过过滤后的窗口显示:
SELECT
ROW_NUMBER() OVER (ORDER BY "mymodel"."order_by" ASC) AS "row"
LAG("mymodel"."id", 1) OVER (ORDER BY "mymodel"."order_by" ASC) AS "prior,
LEAD("mymodel"."id", 1) OVER (ORDER BY "mymodel"."order_by" ASC) AS "next",
"id", "order_by" ....
FROM (
SELECT DISTINCT
"mymodel"."id", "mymodel"."order_by" ....
FROM "mymodel"
INNER JOIN "relatedmodel" ON ("mymodel"."id" = "relatedmodel"."mymodel_id")
WHERE ("related_model"."field"::text LIKE '%X%')
ORDER BY "mymodel"."order_by" ASC
) AS QS
哪个工作精美,再次返回与qs1相同的行数。
在SELECT中仅添加一个窗口函数会导致DISTINCT由于某种原因而失败。 DISTINCT可以在没有窗口函数的情况下正常工作(仅返回唯一的mymodel行),但是添加窗口函数可以解决此问题。
使用过滤器作为window函数的子查询。
Django支持子查询,但是我找不到在这里应用子查询的方法。
所以我想知道是否有办法做到这一点。要将注释用作QuerySet的包装,而不是查询集中的其他列。