현재 null을 포함하는 왼쪽 조인으로 필터를 수행하는 방법을 알아 내려고하고 있습니다. 여기에 내가 일하고 있어요 스키마의 단순화 버전이다 : A는 bookclub_id
및 reviewer_id
주어진 모든 책을 나에게 돌려 위해 그들이> = 3을 평가 한 것을 쿼리는포스트 그레스에서 외부 조인 후 nulls 필터링
CREATE TABLE bookclubs (
bookclub_id UUID NOT NULL PRIMARY KEY
);
CREATE TABLE books (
bookclub_id UUID NOT NULL,
book_id UUID NOT NULL
);
ALTER TABLE books ADD CONSTRAINT books_pk PRIMARY KEY(bookclub_id, book_id);
ALTER TABLE books ADD CONSTRAINT book_to_bookclub FOREIGN KEY(bookclub_id)
REFERENCES bookclubs(bookclub_id) ON UPDATE NO ACTION ON DELETE CASCADE;
CREATE INDEX books_bookclub_index ON books (bookclub_id);
CREATE TABLE book_reviews (
bookclub_id UUID NOT NULL,
book_id UUID NOT NULL,
reviewer_id TEXT NOT NULL,
rating int8 NOT NULL
);
ALTER TABLE book_reviews ADD CONSTRAINT book_reviews_pk PRIMARY KEY(bookclub_id, book_id, reviewer_id);
ALTER TABLE book_reviews ADD CONSTRAINT book_review_to_book FOREIGN KEY(bookclub_id,book_id)
REFERENCES books(bookclub_id,book_id) ON UPDATE NO ACTION ON DELETE CASCADE;
CREATE INDEX book_review_to_book_index ON book_reviews (bookclub_id, book_id);
CREATE INDEX book_review_by_reviewer ON book_reviews (bookclub_id, reviewer_id, rating);
내가 원하는, 또는 그들이 평가하지 않았습니다. 그들이 평가하지 않은 도서에는 book_reviews
테이블에 항목이 없으며 이는 내가 할 수있는 일이 아닙니다. rating
은 관련성이있는 경우 실제로 열거 형이지만 실제로는 그렇지 않습니다. 명백한 일을에서
내 첫 번째 시도는 실패
SELECT *
FROM books
LEFT OUTER JOIN book_reviews
ON (((books.bookclub_id = book_reviews.bookclub_id)
AND (books.book_id = book_reviews.book_id))
AND (book_reviews.reviewer_id = 'alice'))
WHERE books.bookclub_id = '00000000-0000-0000-0000-000000000000'
AND book_reviews.rating != 1
AND book_reviews.rating != 2;
이는 WHERE
조건이 실제로 어떻게 구현되는지에 대해 한 번 내가 생각하는 어떤 의미를 만드는 사용자의 리뷰가없는 책 삭제합니다. 여기
Nested Loop (cost=0.30..16.39 rows=1 width=104)
-> Index Scan using book_reviews_pk on book_reviews (cost=0.15..8.21 rows=1 width=72)
Index Cond: ((bookclub_id = '00000000-0000-0000-0000-000000000000'::uuid) AND (reviewer_id = 'alice'::text))
Filter: ((rating <> 1) AND (rating <> 2))
-> Index Only Scan using books_pk on books (cost=0.15..8.17 rows=1 width=32)
Index Cond: ((bookclub_id = '00000000-0000-0000-0000-000000000000'::uuid) AND (book_id = book_reviews.book_id))
그래서 내가 널 위해 명시 적 검사를 추가 쿼리 계획이다 :
는
SELECT *
FROM books
LEFT OUTER JOIN book_reviews
ON (((books.bookclub_id = book_reviews.bookclub_id)
AND (books.book_id = book_reviews.book_id))
AND (book_reviews.reviewer_id = 'alice'))
WHERE books.bookclub_id = '00000000-0000-0000-0000-000000000000'
AND book_reviews.rating IS NULL
OR (book_reviews.rating != 1
AND book_reviews.rating != 2);
이 올바른 결과를 반환하지만 끔찍하게 비효율적 인 것으로 나타나고 중단에 DB를 갈기. 여기에 내가이 일을 해석에는 전문가는 아니지만 쿼리 계획
Hash Left Join (cost=18.75..52.56 rows=1346 width=104)
Hash Cond: ((books.bookclub_id = book_reviews.bookclub_id) AND (books.book_id = book_reviews.book_id))
Filter: (((books.bookclub_id = '00000000-0000-0000-0000-000000000000'::uuid) AND (book_reviews.rating IS NULL)) OR ((book_reviews.rating <> 1) AND (book_reviews.rating <> 2)))
-> Seq Scan on books (cost=0.00..23.60 rows=1360 width=32)
-> Hash (cost=18.69..18.69 rows=4 width=72)
-> Bitmap Heap Scan on book_reviews (cost=10.23..18.69 rows=4 width=72)
Recheck Cond: (reviewer_id = 'alice'::text)
-> Bitmap Index Scan on book_review_by_reviewer (cost=0.00..10.22 rows=4 width=0)
Index Cond: (reviewer_id = 'alice'::text)
하지만 그 Filter
외부 나쁜 것 같습니다에 이동. 원하는 결과를 얻을 수 있도록 쿼리를 구조화하는 효율적인 방법이 있습니까? 감사합니다
답변 주셔서 감사합니다.하지만 제대로 작동하지 않는 것 같습니다. 나는 여전히 리뷰 내용을 nulls로 필터링해야하는 행을 얻는다. https://gist.github.com/drapp/0e9b09fe97f99a27fa1dde2683df7316 –
@DouglasRapp 이제는 문제를 이해할 수 있다고 생각한다. 나는 오늘 시간이 없지만 내일 시도 할 것이다. –