Ответ 1
Добавление массивов вместе сохранит порядок:
@nonull = Photo.where("collection_id is not null").order("collection_id desc")
@yesnull = Photo.where("collection_id is null")
@wanted = @[email protected]
В моем приложении Rails я столкнулся с проблемой пару раз, чтобы узнать, как другие люди решают:
У меня есть определенные записи, где значение является необязательным, поэтому некоторые записи имеют значение, а некоторые - для этого столбца.
Если я заказываю по этому столбцу в некоторых базах данных сначала сортировку нулей, а в некоторых базах - сортировку нулей.
Например, у меня есть Фотографии, которые могут или не могут принадлежать коллекции, т.е. есть некоторые фотографии, где collection_id=nil
и некоторые, где collection_id=1
и т.д.
Если я делаю Photo.order('collection_id desc)
, то на SQLite я получаю нулевые значения, но на PostgreSQL я сначала получаю нули.
Есть ли хороший стандартный Rails способ справиться с этим и получить согласованную производительность в любой базе данных?
Добавление массивов вместе сохранит порядок:
@nonull = Photo.where("collection_id is not null").order("collection_id desc")
@yesnull = Photo.where("collection_id is null")
@wanted = @[email protected]
Я не эксперт по SQL, но почему бы просто не отсортировать, если что-то сначала пустое, а затем сортировать по его сортировке.
Photo.order('collection_id IS NULL, collection_id DESC') # Null last
Photo.order('collection_id IS NOT NULL, collection_id DESC') # Null first
Если вы используете PostgreSQL, вы также можете сделать это
Photo.order('collection_id DESC NULLS LAST') #Null Last
Photo.order('collection_id DESC NULLS FIRST') #Null First
Но SQLite3 даст вам ошибки.
Поместите знак минус перед именем column_name и измените направление заказа. Он работает на mysql. Подробнее
Product.order('something_date ASC') # NULLS came first
Product.order('-something_date DESC') # NULLS came last
Photo.order('collection_id DESC NULLS LAST')
Я знаю, что это старый, но я нашел этот фрагмент, и он работает для меня.
Несмотря на то, что в 2017 году пока еще нет консенсуса относительно того, следует ли иметь приоритет NULL
. Если вы не будете откровенно об этом, ваши результаты будут варьироваться в зависимости от СУБД.
В стандарте не указывается, как NULL следует заказывать по сравнению с значениями, отличными от NULL, за исключением того, что любые два NULL должны считаться одинаково упорядоченными и что NULL должны сортировать либо выше, либо ниже всех значений, отличных от NULL.
Чтобы проиллюстрировать проблему, я собрал список нескольких наиболее популярных случаев, когда речь заходит о разработке Rails:
NULL
имеют наибольшее значение.
По умолчанию нулевые значения сортируются как большие, чем любое ненулевое значение.
NULL
имеют самое низкое значение.
При выполнении ORDER BY значения NULL отображаются сначала, если вы выполняете ORDER BY... ASC и последний, если вы выполняете ORDER BY... DESC.
NULL
имеют самое низкое значение.
Строка со значением NULL выше, чем строки с регулярными значениями в порядке возрастания, и она обращается в порядке убывания.
К сожалению, Rails сам по себе пока не дает решения.
Для PostgreSQL вы можете довольно интуитивно использовать:
Photo.order('collection_id DESC NULLS LAST') # NULLs come last
Для MySQL вы можете поместить знак минус вверх, но эта функция, похоже, недокументирована. Появляется для работы не только с числовыми значениями, но и с датами.
Photo.order('-collection_id DESC') # NULLs come last
Чтобы обойти их оба, это работает:
Photo.order('collection_id IS NULL, collection_id DESC') # NULLs come last
Тем не менее, этот не работает в SQLite.
Чтобы обеспечить кросс-поддержку для всех СУБД, вам нужно написать запрос, используя CASE
, уже предложенный @PhilIT:
Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')
который преобразуется в первую сортировку каждой из записей сначала с помощью результатов CASE
(по умолчанию возрастающий порядок, что означает, что значения NULL
будут последними), второй - calculation_id
.
Бит опоздал на показ, но есть общий способ SQL для этого. Как обычно, CASE
для спасения.
Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')
Самый простой способ - использовать:
.order('name nulls first')
Для потомков я хотел выделить ошибку ActiveRecord, относящуюся к NULLS FIRST
.
Если вы попытаетесь позвонить:
Model.scope_with_nulls_first.last
Rails попытается вызвать reverse_order.first
, а reverse_order
не совместим с NULLS LAST
, поскольку он пытается сгенерировать недопустимый SQL:
PG::SyntaxError: ERROR: syntax error at or near "DESC"
LINE 1: ...dents" ORDER BY table_column DESC NULLS LAST DESC LIMIT...
Это было упомянуто несколько лет назад в некоторых нерешенных проблемах Rails (один, два, три). Я смог обойти это, выполнив следующие действия:
scope :nulls_first, -> { order("table_column IS NOT NULL") }
scope :meaningfully_ordered, -> { nulls_first.order("table_column ASC") }
Похоже, что, объединив два порядка вместе, генерируется правильный SQL:
Model Load (12.0ms) SELECT "models".* FROM "models" ORDER BY table_column IS NULL DESC, table_column ASC LIMIT 1
Единственным недостатком является то, что эта цепочка должна выполняться для каждой области.
В моем случае мне нужны строки сортировки по дате начала и окончания ASC, но в некоторых случаях end_date был пустым и что строки должны быть выше, я использовал
@invoice.invoice_lines.order('start_date ASC, end_date ASC NULLS FIRST')
Похоже, вам нужно будет сделать это в Ruby, если вы хотите получить согласованные результаты по типам баз данных, поскольку сама база данных интерпретирует, идут ли NULLS в начале или конце списка.
Photo.all.sort {|a, b| a.collection_id.to_i <=> b.collection_id.to_i}
Но это не очень эффективно.