Случайное перемешивание результатов запроса с одинаковыми значениями

#python #sqlalchemy #flask-sqlalchemy

#python #sqlalchemy #flask-sqlalchemy

Вопрос:

Выполнение order_by операции над запросом SQLAlchemy, где сортируемые объекты имеют одинаковое значение, всегда возвращает их в том порядке, в котором они находятся в базе данных.

Есть ли функция для дополнительного перемешивания тех результатов, которые совпадают?


Практический пример:

 ITEM         PRICE

hamburger    20
burrito      10
sandwich     10
quesadilla   15
 

Запуск db.session.query(FoodItem).order_by(FoodItem.price).all() вернет их в следующем порядке:

  1. Гамбургер
  2. Кесадилья
  3. Буррито
  4. Сандвичи

burrito всегда будет предшествовать sandwich , как и раньше в базе данных, независимо от того, сколько раз вы запускали функцию.

Я попытался случайным образом отсортировать результаты перед заказом, как так:

 from sqlalchemy.sql.expression import func

db.session.query(FoodItem).order_by(func.random()).order_by(FoodItem.price).all()
 

Однако это не дает желаемого эффекта — окончательный заказ по-прежнему сохраняется в соответствии с базой данных. Есть ли способ — sqlalchemy или лямбда-выражение / функция Python — для достижения желаемого результата отсортированных элементов с элементами, имеющими одинаковые значения в случайном порядке?

Комментарии:

1. «всегда возвращает их в том порядке, в котором они находятся в базе данных» — это не совсем верно или гарантировано. Например, план параллельного запроса может перепутать порядок. Иными словами: в SQL нет порядка в базе данных. Единственный способ гарантировать порядок — это использовать ORDER BY . Конечно, реализация может большую часть времени считывать строки так, как будто они находятся на диске, но порядок дисков также может меняться, и разные планы читаются по-разному.

Ответ №1:

При передаче нескольких критериев необходимо использовать правильный order_by() порядок вызовов; критерии упорядочены от наиболее значимых к наименьшим, первый order_by параметр применяется ко всем строкам, второй — только к строкам, где первый параметр равен.

Итак, если нужно рандомизировать только товары с одинаковой ценой, поместите случайный порядок последним:

 db.session.query(FoodItem).order_by(FoodItem.price).order_by(func.random())
 

Обратите внимание, что для этого Query.order_by() требуется несколько критериев, поэтому вам не нужно использовать несколько вызовов:

 db.session.query(FoodItem).order_by(FoodItem.price, func.random())
 

Таким образом, строки сначала сортируются по цене, затем, если две строки имеют одинаковую цену, используется следующий критерий, и поэтому они рандомизируются только в пределах одной ценовой группы.

Для дальнейшего использования: распечатайте свой объект запроса (без .all() ), чтобы получить представление о том, что генерируется SQL; например, используя мое лучшее предположение о том, как может выглядеть ваша модель, ваш запрос выдает:

 SELECT food_item.id AS food_item_id, food_item.name AS food_item_name, food_item.price AS food_item_price
FROM food_item ORDER BY random(), food_item.price
 

и, поменяв местами критерии, вы получите:

 SELECT food_item.id AS food_item_id, food_item.name AS food_item_name, food_item.price AS food_item_price
FROM food_item ORDER BY food_item.price, random()
 

Примечание: random() специфично для PostgreSQL и SQLite, если вам нужно поддерживать другие базы данных, вам нужно будет настроить выражение.