現在、NULLを含む左結合でフィルタを実行する方法を理解しようとしています。ここで私が働いているスキーマの簡略化 バージョンがあります:bookclub_id
とreviewer_id
与えられ、すべての書籍を私に返すために、彼らは> = 3を評価したので、クエリはpostgresの外部結合後にNULLをフィルタリングする
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))
だだから私はnullのための明示的なチェックを追加しました:
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私は今問題を理解していると思う。今日は時間がないが、明日に試してみる。 –