Рельсы: заказ с нулями последний



В моем приложении Rails я столкнулся с проблемой пару раз, что я хотел бы знать, как другие люди решают:



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



Если я заказываю по этому столбцу на некоторых базах данных нули сортируются первыми, а на некоторых базах данных нули сортируются последними.



например, у меня есть фотографии, которые могут или не могут принадлежать к коллекции, т. е. есть несколько фотографий, где collection_id=nil а кое где collection_id=1 etc.



Если я это сделаю Photo.order('collection_id desc) затем на SQLite я получаю нули в последнюю очередь, но на PostgreSQL я получаю нули в первую очередь.



есть ли хороший, стандартный способ Rails справиться с этим и получить согласованную производительность в любой базе данных?

714   11  

11 ответов:

добавление массивов вместе сохранит порядок:

@nonull = Photo.where("collection_id is not null").order("collection_id desc")
@yesnull = Photo.where("collection_id is null")
@wanted = @nonull+@yesnull

http://www.ruby-doc.org/core/classes/Array.html#M000271

Я не эксперт в SQL, но почему бы просто не отсортировать, если что-то сначала null, а затем сортировать по тому, как вы хотели его отсортировать.

Photo.order('collection_id IS NULL, collection_id DESC')  # Null's last
Photo.order('collection_id IS NOT NULL, collection_id DESC') # Null's first

Если вы используете только PostgreSQL, вы также можете сделать это

Photo.order('collection_id DESC NULLS LAST')  #Null's Last
Photo.order('collection_id DESC NULLS FIRST') #Null's First

но SQLite3 даст вам ошибки.

несмотря на то, что сейчас 2017 год, до сих пор нет консенсуса по поводу того,NULLs должно иметь приоритет. Без вашего явного об этом, ваши результаты будут варьироваться в зависимости от СУБД.

стандарт не указывает, как null должны быть упорядочены по сравнению с ненулевыми значениями, за исключением того, что любые два null должны считаться одинаково упорядоченными, и что null должны сортировать либо выше, либо ниже всех ненулевых значений.

источник, сравнение большинства СУБД

чтобы проиллюстрировать проблему, я составил список из нескольких наиболее популярных случаев, когда речь заходит о разработке Rails:

PostgreSQL

NULLs имеют самое высокое значение.

по умолчанию значения null сортируются так, как будто они больше любого ненулевого значения.

источник: PostgreSQL документация

MySQL

NULLs имеют самое низкое значение.

при выполнении ORDER BY сначала отображаются нулевые значения, если вы выполняете ORDER BY ... ASC и последний, если вы делаете заказ ... ОПИСАНИЕ.

источник: MySQL documentation

SQLite

NULLs имеют самое низкое значение.

строка с нулевым значением выше, чем строки с регулярные значения в порядке возрастания, и он отменяется для нисходящего порядка.

источник

решение

к сожалению, Rails сам по себе еще не дает решения для этого.

PostgreSQL specific

для PostgreSQL вы можете вполне интуитивно использовать:

Photo.order('collection_id DESC NULLS LAST') # NULLs come last

MySQL specific

для MySQL вы можете поставить знак минус заранее, но это можно без документов. Похоже, что он работает не только с числовыми значениями, но и с датами.

Photo.order('-collection_id DESC') # NULLs come last

PostgreSQL и MySQL specific

чтобы прикрыть их обоих, это, кажется, работает:

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.

Put минус перед 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')

Я знаю, что это старый, но я только что нашел этот фрагмент, и он работает для меня.

немного поздно для шоу, но есть общий способ SQL, чтобы сделать это. Как обычно, CASE к спасению.

Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')

ради потомства, я хотел бы выделить ошибку 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

единственным недостатком является то, что эта цепочка должна быть сделана для каждого объем.

самый простой способ-это использовать:

.order('name nulls first')

в моем случае мне нужно было сортировать строки по дате начала и окончания по ASC, но в некоторых случаях end_date был равен нулю, и эти строки должны быть выше, я использовал

@invoice.invoice_lines.order('start_date ASC, end_date ASC NULLS FIRST')

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

Photo.all.sort {|a, b| a.collection_id.to_i <=> b.collection_id.to_i}

но это не очень эффективно.

сначала получить фотографии, где collection_id не null и приказа collection_id по убыванию.

тогда получить фотографии, где collection_id равно null и добавьте их.

Photos.where.not(collection_id: [nil, ""])
  .order(collection_id: :desc) + where(collection_id: [nil, ""])

Comments

    Ничего не найдено.