что вам может быть интересно для вдохновения, если вы хотите обойти немного более высокий уровень, чем обычный SQL.
ИТЬ
Благодаря опубликованному ответу я нашел гораздо более простой способ сформулировать проблему. Оригинальный вопрос можно увидеть в истории изменений.
Эта проблемаЯ пытаюсь перевести запрос SQL на Django, но получаю ошибку, которую не понимаю.
Вот модель Django, которую я имею:
class Title(models.Model):
title_id = models.CharField(primary_key=True, max_length=12)
title = models.CharField(max_length=80)
publisher = models.CharField(max_length=100)
price = models.DecimalField(decimal_places=2, blank=True, null=True)
У меня есть следующие данные:
publisher title_id price title
--------------------------- ---------- ------- -----------------------------------
New Age Books PS2106 7 Life Without Fear
New Age Books PS2091 10.95 Is Anger the Enemy?
New Age Books BU2075 2.99 You Can Combat Computer Stress!
New Age Books TC7777 14.99 Sushi, Anyone?
Binnet & Hardley MC3021 2.99 The Gourmet Microwave
Binnet & Hardley MC2222 19.99 Silicon Valley Gastronomic Treats
Algodata Infosystems PC1035 22.95 But Is It User Friendly?
Algodata Infosystems BU1032 19.99 The Busy Executive's Database Guide
Algodata Infosystems PC8888 20 Secrets of Silicon Valley
Вот что я хочу сделать: ввести аннотированное полеdbl_price
что вдвое превышает цену, затем сгруппируйте полученный набор запросов поpublisher
и для каждого издателя вычислите сумму всехdbl_price
значения для всех названий, опубликованных этим издателем.
SQL-запрос, который делает это, выглядит следующим образом:
SELECT SUM(dbl_price) AS total_dbl_price, publisher
FROM (
SELECT price * 2 AS dbl_price, publisher
FROM title
) AS A
GROUP BY publisher
Желаемый результат будет:
publisher tot_dbl_prices
--------------------------- --------------
Algodata Infosystems 125.88
Binnet & Hardley 45.96
New Age Books 71.86
Джанго запросЗапрос будет выглядеть так:
Title.objects
.annotate(dbl_price=2*F('price'))
.values('publisher')
.annotate(tot_dbl_prices=Sum('dbl_price'))
но выдает ошибку:
KeyError: 'dbl_price'.
что указывает на то, что он не может найти полеdbl_price
в наборе запросов.
Вот почему эта ошибка происходит:документация говорит
Следует также отметить, что средняя_категория была явно включена в список значений, которые должны быть возвращены. Это необходимо из-за упорядочения предложений values () и annotate ().
Если предложение values () предшествует предложению annotate (), любые аннотации будут автоматически добавлены в набор результатов. Однако, если предложение values () применяется после предложения annotate (), необходимо явно включить столбец агрегирования.
Итакdbl_price
не удалось найти в агрегации, поскольку он был созданannotate
, но не был включен вvalues()
.
Тем не менее, я не могу включить его вvalues
либо, потому что я хочу использоватьvalues
(сопровождаемый другимannotate
) как группирующее устройство, так как
Если предложение values () предшествует annotate (), аннотация будет вычислена с использованием группировки, описанной предложением values ().
которая является основой того, как Джангореализует SQLGROUP BY
, Это означает, что я не могу включитьdbl_price
внутриvalues()
потому что тогда группировка будет основываться на уникальных комбинациях обоих полейpublisher
а такжеdbl_price
в то время как мне нужно сгруппировать поpublisher
только.
Итак, следующий запрос, который отличается от приведенного выше только тем, что я агрегирую по моделямprice
поле, а не аннотированныйdbl_price
поле, на самом деле работает:
Title.objects
.annotate(dbl_price=2*F('price'))
.values('publisher')
.annotate(sum_of_prices=Count('price'))
посколькуprice
поле находится в модели, а не является аннотированным полем, поэтому нам не нужно включать его вvalues
сохранить его в наборе запросов.
Итак, у нас есть это: мне нужно включить аннотированное свойство вvalues
чтобы сохранить его в наборе запросов, но я не могу этого сделать, потому чтоvalues
также используется для группировки (что будет неправильно с дополнительным полем). Проблема, по сути, связана с двумя очень разными способамиvalues
используется в Django, в зависимости от контекста (независимо от того,values
сопровождаетсяannotate
) - который является (1) извлечением значения (обычный SQL)SELECT
список) и (2) группировка + агрегирование по группам (SQLGROUP BY
) - и в этом случае эти два способа кажутся противоречивыми.
Мой вопрос: есть ли способ решить эту проблему (без таких вещей, как возврат к raw SQL)?
Пожалуйста, обратите внимание: конкретный пример может быть решен путем перемещения всехannotate
заявления послеvalues
, который был отмечен несколькими ответами. Тем не менее, меня больше интересуют решения (или обсуждение), которые быannotate
заявление передvalues()
По трем причинам: 1. Существуют также более сложные примеры, где предложенный обходной путь не будет работать. 2. Я могу представить себе ситуации, когда аннотированный набор запросов был передан другой функции, которая фактически выполняет GROUP BY, так что единственное, что мы знаем, это набор имен аннотированных полей и их типы. 3. Ситуация кажется довольно простой, и меня удивило бы, если бы это столкновение двух разных видов использованияvalues()
не было замечено и обсуждено прежде.