Rails : 마지막에 null이있는 주문
내 Rails 앱에서 다른 사람들이 어떻게 해결하는지 알고 싶은 문제가 몇 번 발생했습니다.
값이 선택 사항 인 특정 레코드가 있으므로 일부 레코드에는 값이 있고 일부 레코드는 해당 열에 대해 null입니다.
일부 데이터베이스에서 해당 열을 기준으로 정렬하면 null이 먼저 정렬되고 일부 데이터베이스에서는 null이 마지막으로 정렬됩니다.
예를 들어, 컬렉션에 속할 수도 있고 아닐 수도있는 사진이 있습니다. 즉, 사진이 어디에 collection_id=nil
있고 일부가 어디에 있는지 collection_id=1
등이 있습니다.
그런 Photo.order('collection_id desc)
다음 SQLite에서 마지막으로 null을 얻지 만 PostgreSQL에서는 먼저 null을 얻습니다.
이를 처리하고 모든 데이터베이스에서 일관된 성능을 얻을 수있는 멋진 표준 Rails 방법이 있습니까?
배열을 함께 추가하면 순서가 유지됩니다.
@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
보편적 인 것을 원한다면 (여러 데이터베이스에서 동일한 쿼리를 사용하는 것과 같이 (@philT 제공))
Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')
지금은 2017 년이지만, NULL
s가 우선해야하는지 에 대한 합의는 아직 없습니다 . 그것에 대해 명시하지 않으면 결과는 DBMS에 따라 달라질 것입니다.
표준은 NULL이 아닌 값과 비교하여 NULL을 정렬하는 방법을 지정하지 않습니다. 단 두 개의 NULL이 동일한 순서로 간주되고 NULL이 모든 NULL이 아닌 값의 위 또는 아래로 정렬되어야한다는 점을 제외하고는 예외입니다.
문제를 설명하기 위해 Rails 개발과 관련하여 가장 많이 사용되는 몇 가지 사례 목록을 작성했습니다.
PostgreSQL
NULL
의 값이 가장 높습니다.
기본적으로 null 값은 null이 아닌 값보다 큰 것처럼 정렬됩니다.
MySQL
NULL
의 값이 가장 낮습니다.
ORDER BY를 수행 할 때 ORDER BY ... ASC를 수행하면 NULL 값이 먼저 표시되고 ORDER BY ... DESC를 수행하면 마지막으로 표시됩니다.
SQLite
NULL
의 값이 가장 낮습니다.
NULL 값이있는 행은 오름차순으로 일반 값이있는 행보다 높으며 내림차순으로 반전됩니다.
해결책
불행히도 Rails 자체는 아직 이에 대한 솔루션을 제공하지 않습니다.
PostgreSQL 관련
PostgreSQL의 경우 매우 직관적으로 사용할 수 있습니다.
Photo.order('collection_id DESC NULLS LAST') # NULLs come last
MySQL 특정
MySQL의 경우 마이너스 기호를 미리 입력 할 수 있지만이 기능은 문서화되지 않은 것 같습니다. 숫자 값뿐만 아니라 날짜에서도 작동하는 것처럼 보입니다.
Photo.order('-collection_id DESC') # NULLs come last
PostgreSQL 및 MySQL 관련
두 가지를 모두 다루려면 다음과 같이 작동합니다.
Photo.order('collection_id IS NULL, collection_id DESC') # NULLs come last
그러나 이것은 SQLite에서 작동하지 않습니다 .
범용 솔루션
모든 DBMS에 대한 교차 지원을 제공하려면 CASE
이미 @PhilIT에서 제안한를 사용하여 쿼리를 작성해야합니다 .
Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')
이는 각 레코드를 먼저 CASE
결과 (기본적으로 오름차순, 즉 NULL
값이 마지막 값이 됨을 의미 함) 별로 먼저 정렬하고 , 두 번째는 calculation_id
.
넣고 빼기 기호를 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')
I know this is an old one but I just found this snippet and it works for me.
Bit late to the show but there is a generic SQL way to do it. As usual, CASE
to the rescue.
Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')
For posterity's sake, I wanted to highlight an ActiveRecord error relating to NULLS FIRST
.
If you try to call:
Model.scope_with_nulls_first.last
Rails will attempt to call reverse_order.first
, and reverse_order
is not compatible with NULLS LAST
, as it tries to generate the invalid SQL:
PG::SyntaxError: ERROR: syntax error at or near "DESC"
LINE 1: ...dents" ORDER BY table_column DESC NULLS LAST DESC LIMIT...
This was referenced a few years ago in some still-open Rails issues (one, two, three). I was able to work around it by doing the following:
scope :nulls_first, -> { order("table_column IS NOT NULL") }
scope :meaningfully_ordered, -> { nulls_first.order("table_column ASC") }
It appears that by chaining the two orders together, valid SQL gets generated:
Model Load (12.0ms) SELECT "models".* FROM "models" ORDER BY table_column IS NULL DESC, table_column ASC LIMIT 1
The only downside is that this chaining has to be done for each scope.
The easiest way is to use:
.order('name nulls first')
In my case I needed sort lines by start and end date by ASC, but in few cases end_date was null and that lines should be in above, I used
@invoice.invoice_lines.order('start_date ASC, end_date ASC NULLS FIRST')
First get Photos where collection_id
is not null and order by collection_id
descending.
Then get Photos where collection_id
is null and add them.
Photos.where.not(collection_id: [nil, ""])
.order(collection_id: :desc) + where(collection_id: [nil, ""])
It seems like you'd have to do it in Ruby if you want consistent results across database types, as the database itself interprets whether or not the NULLS go at the front or end of the list.
Photo.all.sort {|a, b| a.collection_id.to_i <=> b.collection_id.to_i}
But that is not very efficient.
참고URL : https://stackoverflow.com/questions/5826210/rails-order-with-nulls-last
'developer tip' 카테고리의 다른 글
프로그래밍 방식으로 UIView의 고정 높이 제약 조건을 업데이트하는 방법은 무엇입니까? (0) | 2020.10.15 |
---|---|
UITextField에서 텍스트 들여 쓰기 (0) | 2020.10.15 |
탐색 창에서 메뉴 항목의 텍스트 색상 변경 (0) | 2020.10.15 |
전 처리기 지시문에서 ## (이중 해시)는 무엇을합니까? (0) | 2020.10.14 |
온라인 IDE에서 이상하게 작동하는 프로그램 (0) | 2020.10.14 |