Набор запросов для sql-запроса

Описание проблемы, пример данных и запрос postgres см. в Filtering unique values. Я хотел бы преобразовать SQL в набор запросов. Мне кажется, что я близок к этому, но не совсем.

SELECT Column_A, Column_B, Column_C, 0 as RN
FROM TABLE
WHERE COLUMN_C is null and Column_B in (UserA, UserB, UserC)

UNION ALL

SELECT Column_A, Column_B, Column_C, RN
FROM (
  SELECT A.*, ROW_NUMBER() over (partition by A.column_C Order by case A.column_B when 'UserA' then 0 else 1 end, U.Time_Created) rn
  FROM Table A
  INNER JOIN user U
    on U.Column_B = A.Column_B
  WHERE A.Column_C is not null and ColumnB in (userA, userB, UserC)) B
WHERE RN = 1

Вот что у меня есть на данный момент:

qs1 = Table.objects.filter(Column_C__isnull=True).annotate(rn=Value(0))

qs2 = Table.objects.annotate(rn=Window(
   expression=RowNumber(),
   partition_by=[Column_C],
   order_by=[Case(When(Column_B=UserA, then=0), default=1), 'Table_for_Column_B__time_created']
)).filter(Column_C__isnull=False, rn=1)

return qs2.union(qs1)

Это не совсем работает.

django.db.utils.NotSupportedError: Window is disallowed in the filter clause.

Далее я попробовал вытащить промежуточный результат в подзапрос, чтобы обеспечить фильтрацию во внешнем запросе, поскольку мне действительно нужны только строки с номером строки = 1.

qs1 = Table.objects.filter(Column_C__isnull=True).annotate(rn=Value(0))

qs2 = Table.objects.annotate(rn=Window(
   expression=RowNumber(),
   partition_by=[Column_C],
   order_by=[Case(When(Column_B=UserA, then=0), default=1), 'Table_for_Column_B__time_created']
)).filter(pk=OuterRef('pk'))

qs3 = Table.objects.annotate(rn=Subquery(qs2.values('rn'))).filter(Column_C__isnull=False, rn=1)

return qs3.union(q1)

На этот раз исключений нет, но это не работает. Каждая строка в таблице получает аннотацию row_number=1. Из исходного примера queryset возвращает все 7 строк вместо фильтрации до 5.

  1. Is it possible to filter on window expressions?
  2. What's the best practices to keep in mind when converting window queries to subqueries?
  3. Is there a better way to structure the queryset?

Вы должны быть в состоянии сделать это без оконного выражения, используя SubQuery

Сначала создайте набор запросов для подзапроса, который упорядочивается по соответствию Column_B=UserA, а затем time_created

from django.db.models import Case, When, Q, Subquery, OuterRef

tables_ordered = Table.objects.filter(
    Column_C=OuterRef('Column_C')
).annotate(
    user_match=Case(When(Column_B=UserA, then=0), default=1)
).order_by('user_match', 'time_created')

Затем этот подзапрос возвращает первый пк для совпавшего Column_C из OuterRef, аналогично выбору первой строки из вашей оконной функции

first_pk_for_each_column_c = Subquery(tables_ordered.values('pk')[:1])

Затем используйте два объекта Q для создания OR, который выбирает строку, если Column_C является NULL или pk соответствует первому pk из подзапроса

Table.objects.filter(
    Q(Column_C__isnull=True) | Q(pk=first_pk_for_each_column_c)
)
Вернуться на верх