Rails : 마지막에 null이있는 주문


83

내 Rails 앱에서 다른 사람들이 어떻게 해결하는지 알고 싶은 문제가 몇 번 발생했습니다.

값이 선택 사항 인 특정 레코드가 있으므로 일부 레코드에는 값이 있고 일부 레코드는 해당 열에 대해 null입니다.

일부 데이터베이스에서 해당 열을 기준으로 정렬하면 null이 먼저 정렬되고 일부 데이터베이스에서는 null이 마지막으로 정렬됩니다.

예를 들어, 컬렉션에 속할 수도 있고 아닐 수도있는 사진이 있습니다. 즉, 사진이 어디에 collection_id=nil있고 일부가 있습니다 collection_id=1.

그런 Photo.order('collection_id desc)다음 SQLite에서 마지막으로 null을 얻지 만 PostgreSQL에서는 먼저 null을 얻습니다.

이를 처리하고 모든 데이터베이스에서 일관된 성능을 얻을 수있는 멋진 표준 Rails 방법이 있습니까?

답변:


0

배열을 함께 추가하면 순서가 유지됩니다.

@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


2
글쎄, 나는이 아이디어가 마음에 들지 않지만 이것이 효과가있을 것이라고 생각한다. 너무 오래 열어서 죄송합니다. 다른 답변이 나오기를 바랐습니다. 그러나 그것에 대해 생각하는 데 더 많은 시간을 소비 한 나는 이것이 사진 모델의 방법으로 만들어 질 수 있다고 생각하고 그렇게 나쁘지 않을 것입니다.
Andrew

mysql을 사용하는 경우 쉽습니다. 내 솔루션을 참조하십시오.
Jacob

맞다, 내가 찾을 수있는 가장 불가지론적인 방법 일 뿐이라고 말 했어야했다.
에릭

8
이것은 where배열을 반환하지 않고 ActiveRecord :: Relation을 반환하고 결과를 배열에 강제로 넣으면 표준 ActiveRecord :: Relation을 예상하는 모든 것이 실패 (예 : 페이지 매김)하기 때문에 이것은 나쁜 생각 입니다.
Mike Bethany

1
사실, 사용 가능한 방법이 AR에서 벗어나거나 (완전히) 이식 가능하지 않은 것처럼 보이지만.
에릭

273

저는 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')

22
+1, 허용 된 답변보다 훨씬 낫고 Arel을 통해 표현할 수 있습니다
m_x dec

1
와우, 이것은 오래된 댓글입니다! 그래서 제가 말하려고했던 것은 :p = Photo.arel_table; Photo.order(p[:collection_id].eq(nil)).order(p[:collection_id].desc)
m_x

2
참고 NULLS LAST체인을 허용하지 않습니다 .last()레일 4.2로 쿼리에. 아래에 해결 방법을 게시했습니다.
Lanny Bose 2015 년

1
IS NULLNULL 행 을 넣어 정렬하는 이유는 무엇 입니까? 당신은 그것이 그들을 먼저 넣을 것이라고 생각할 것입니다.
jackocnr

2
@jackocr myguess : IS NULL은 참이면 1, 거짓이면 0, 정렬 됨, 0은 1보다 먼저옵니다 ...
Intentss

37

지금은 2017 년이지만, NULLs가 우선해야하는지 에 대한 합의는 아직 없습니다 . 그것에 대해 명시하지 않으면 결과는 DBMS에 따라 달라질 것입니다.

표준은 NULL이 아닌 값과 비교하여 NULL을 정렬하는 방법을 지정하지 않습니다. 단 두 개의 NULL이 동일한 순서로 간주되고 NULL이 모든 NULL이 아닌 값의 위 또는 아래로 정렬되어야한다는 점을 제외하고는 예외입니다.

소스, 대부분의 DBMS 비교

문제를 설명하기 위해 Rails 개발과 관련하여 가장 많이 사용되는 몇 가지 사례 목록을 작성했습니다.

PostgreSQL

NULL의 값이 가장 높습니다.

기본적으로 null 값은 null이 아닌 값보다 큰 것처럼 정렬됩니다.

출처 : PostgreSQL 문서

MySQL

NULL의 값이 가장 낮습니다.

ORDER BY를 수행 할 때 ORDER BY ... ASC를 수행하면 NULL 값이 먼저 표시되고 ORDER BY ... DESC를 수행하면 마지막으로 표시됩니다.

출처 : MySQL 문서

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.


14
Photo.order('collection_id DESC NULLS LAST')

나는 이것이 오래된 것임을 알고 있지만 방금이 스 니펫을 찾았고 그것은 나를 위해 작동합니다.


어떤 버전의 Ruby / Rails가 작동했는지는 모르겠지만 Ruby 2.5 및 rails 5에서는 작동하지 않는 것 같습니다.
Tasos Anesiadis

13

넣고 빼기 기호를 COLUMN_NAME 앞에와 순서의 방향을 반대로. mysql에서 작동합니다. 자세한 내용은

Product.order('something_date ASC') # NULLS came first
Product.order('-something_date DESC') # NULLS came last

10

쇼에 조금 늦었지만 일반적인 SQL 방식이 있습니다. 평소와 같이 CASE구조합니다.

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

6

가장 쉬운 방법은 다음을 사용하는 것입니다.

.order('name nulls first')


3
이 포스트 그레스에 가장 적합합니다
Sergii Mostovyi

1
이것은 좋은 해결책이지만 ActiveRecord last가 잘못된 DESC를 삽입하는 것처럼 보입니다.
sirvine

6

후손을 위해 .NET과 관련된 ActiveRecord 오류를 강조하고 싶었습니다 NULLS FIRST.

전화를 시도하는 경우 :

Model.scope_with_nulls_first.last

레일은 호출을 시도 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 문제 ( 1 , 2 , 3 ) 에서 언급되었습니다 . 다음을 수행하여 문제를 해결할 수있었습니다.

  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

유일한 단점은이 연결이 범위 에 대해 수행되어야한다는 것입니다 .


2

제 경우에는 ASC의 시작 및 종료 날짜별로 정렬 라인이 필요했지만, 드물게 end_date가 null이고 해당 라인이 위에 있어야합니다.

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


-3

데이터베이스 자체가 NULLS가 목록의 맨 앞 또는 끝에 있는지 여부를 해석하기 때문에 데이터베이스 유형간에 일관된 결과를 원한다면 Ruby에서 수행해야하는 것 같습니다.

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

그러나 그것은 그다지 효율적이지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.