NOT NIL을 사용하는 상태의 레일


359

rails 3 스타일을 사용하면 어떻게 반대편을 쓸 수 있습니까?

Foo.includes(:bar).where(:bars=>{:id=>nil})

id가 nil이 아닌 곳을 찾고 싶습니다. 나는 시도했다 :

Foo.includes(:bar).where(:bars=>{:id=>!nil}).to_sql

그러나 그 결과는 다음과 같습니다.

=> "SELECT     \"foos\".* FROM       \"foos\"  WHERE  (\"bars\".\"id\" = 1)"

그것은 분명히 내가 필요한 것이 아니며 ARel의 버그처럼 보입니다.


2
!nil로 평가 true루비, 그리고 ARel는 번역 true1SQL 쿼리한다. 따라서 생성 된 쿼리는 실제로 요청한 것입니다. 이것은 ARel 버그가 아닙니다.
yuval

답변:


510

Rails 3으로이 작업을 수행하는 정식 방법 :

Foo.includes(:bar).where("bars.id IS NOT NULL")

ActiveRecord 4.0 이상이 추가 where.not되어 다음을 수행 할 수 있습니다.

Foo.includes(:bar).where.not('bars.id' => nil)
Foo.includes(:bar).where.not(bars: { id: nil })

테이블 사이의 범위를 작업 할 때 merge기존 범위를 더 쉽게 사용할 수 있도록 활용 하는 것이 좋습니다.

Foo.includes(:bar).merge(Bar.where.not(id: nil))

또한 includes항상 조인 전략을 선택하지는 않으므로 references여기에서도 사용해야 합니다. 그렇지 않으면 유효하지 않은 SQL로 끝날 수 있습니다.

Foo.includes(:bar)
   .references(:bar)
   .merge(Bar.where.not(id: nil))

1
마지막으로 여기에는 효과가 없습니다. 추가 보석이나 플러그인이 필요합니까? 내가 얻을 : rails undefined method 'not_eq' for :confirmed_at:Symbol..
팀 BAAS

3
@Tim 네, 위에 링크 된 MetaWhere gem입니다.
Adam Lassek

3
나는 다른 보석을 필요로하지 않는 솔루션을 좋아한다 :) 조금 추한 경우에도
oreoshake

1
@oreoshake MetaWhere / Squeel은 가치가 있습니다. 이것은 작은면입니다. 그러나 물론 일반적인 경우를 아는 것이 좋습니다.
Adam Lassek

1
@BKSpurgeon 체인의 where조건은 단순히 당신이 같은 단자 법을 칠 때까지 데이터베이스에 접속하지 않고, 대서양 표준시를 구축하고있다 each거나 to_a. 쿼리 작성은 성능 문제가 아닙니다. 데이터베이스에서 요청한 것은
Adam Lassek

251

ARel의 버그가 아니며 논리의 버그입니다.

여기서 원하는 것은 :

Foo.includes(:bar).where(Bar.arel_table[:id].not_eq(nil))

2
! nil을 '1'로 바꾸는 논리가 무엇인지 궁금합니다.
SooDesuNe

12
추측하자면,! nil은 trueboolean 인을 반환합니다. SQLese로 :id => true당신 id = 1을 데려옵니다.
zetetic

이것은 원시 SQL 조각을 작성하지 않는 좋은 방법입니다. 구문은 Squeel만큼 간결하지 않습니다.
Kelvin

1
sqlite3 으로이 작업을 수행하지 못했습니다. sqlite3보고 싶어요 field_name != 'NULL'.
mjnissim 님이

당신이 포스트 그레스를 사용하지 않는 경우에 당신이 얻을 @zetetic id = 't':
thekingoftruth

36

Rails4의 경우 :

따라서 원하는 것은 내부 조인이므로 조인 조건자를 사용해야합니다.

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

그러나 레코드의 경우 "NOT NULL"조건을 원하면 not 술어를 사용하십시오.

Foo.includes(:bar).where.not(bars: {id: nil})

Select * from Foo Left Outer Join Bars on .. WHERE bars.id IS NOT NULL

이 구문은 사용 중단을보고합니다 (문자열 SQL 스 니펫에 대해 이야기하지만 구문 분석기에서 해시 조건이 문자열로 변경 되었습니까?). 끝에 참조를 추가해야합니다.

Foo.includes(:bar).where.not(bars: {id: nil}).references(:bar)

감가 상각 경고 : 문자열 SQL 스 니펫에서 참조되는 테이블 (하나 중 하나 : ....)을 열망하는 것처럼 보입니다. 예를 들면 다음과 같습니다.

Post.includes(:comments).where("comments.title = 'foo'")

현재 Active Record는 문자열의 테이블을 인식하고 주석을 별도의 쿼리에로드하지 않고 주석 테이블을 쿼리에 참여시키는 것을 알고 있습니다. 그러나 완전한 SQL 파서를 작성하지 않고이 작업을 수행하는 것은 본질적으로 결함이 있습니다. 우리는 SQL 파서를 작성하고 싶지 않기 때문에이 기능을 제거하고 있습니다. 이제부터는 문자열에서 테이블을 참조 할 때 명시 적으로 Active Record에 알려야합니다.

Post.includes(:comments).where("comments.title = 'foo'").references(:comments)

1
references전화 나 도움이!
theblang


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