2014-05-15 37 views
0

我有書籍和流派之間的多對多關係。例如,「霍比特人」的書可能有流派「孩子」,「小說」和「幻想」。如何提高多對多SQL查詢的性能?

這裏的模式:

CREATE TABLE "genre" (
    "id" integer NOT NULL PRIMARY KEY, 
    "name" varchar(50) NOT NULL 
) 
; 
CREATE TABLE "book_genres" (
    "book_id" integer NOT NULL REFERENCES "book" ("id"), 
    "genre_id" integer NOT NULL REFERENCES "genre" ("id"), 
    CONSTRAINT book_genres_pkey PRIMARY KEY (book_id, genre_id) 
) 
; 
CREATE TABLE "book" (
    "id" integer NOT NULL PRIMARY KEY, 
    "name" varchar(255) NOT NULL, 
    "price" real NOT NULL 
) 
; 

而且指標:

CREATE INDEX "book_genres_36c249d7" ON "book_genres" ("book_id"); 
CREATE INDEX "book_genres_33e6008b" ON "book_genres" ("genre_id"); 
CREATE INDEX "book_5a5255da" ON "book" ("price"); 

行數:

  • 流派:30
  • book_genres 80萬
  • 書:200 ,0 00

我正在嘗試在SQL中編寫一個查詢,該查詢將按照價格排序的所有書籍帶回所有書籍而不重複。

這裏是我的查詢其做到這一點:

SELECT name, price 
FROM book 
WHERE book.id 
IN 
    (SELECT book_id 
    FROM book_genres 
    WHERE genre_id = 1 
    OR genre_id = 2) 
ORDER BY price LIMIT 10 

我的問題是性能。該查詢最多可能需要2000毫秒才能執行。我怎樣才能提高性能?

我完全控制數據庫(Postgres 9.3),所以可以添加視圖,索引或denormalise。我也使用Django,因此可以使用Python/Django執行多個查詢在內存中執行操作。按價格+ LIMIT

SELECT * 
FROM 
(
    SELECT b.name, b.price 
    FROM book b JOIN book_genres g ON b.book.id = g.book_id 
           AND g.genre_id = 1 
    UNION 

    SELECT b.name, b.price 
    FROM book b JOIN book_genres g ON b.book.id = g.book_id 
           AND g.genre_id = 2 
) 
ORDER BY price LIMIT 10 

回答

3
SELECT b.name, b.price 
FROM book b 
WHERE EXISTS (
    SELECT * 
    FROM book_genres bg 
    WHERE bg.book_id = b.id 
    AND bg.genre_id IN(1 , 2) 
    ) 
ORDER BY b.price 
LIMIT 10 
     ; 

的順序可以是性能殺手:

+0

謝謝,我已經做出了您所建議的更改。性能與我當前的查詢相同。需要注意的一點是,使用'OFFSET'(例如'OFFSET 500'),性能會進一步惡化。計劃者輸出是否有幫助? – donturner

+0

(你在表格修改之後做了真空分析?)刪除'按價格LIMIT xxx'的順序,性能可能會變好(如果沒有太多的行滿足您的條件)OFFSET可能會使事情變得更糟。 – joop

+0

「你做過真空分析嗎?」 - 這是問題!我沒有運行過。現在,我的原始查詢每次不使用「OFFSET」時會運行小於20毫秒,並且在使用時最多可以運行200毫秒(這是可以接受的)。非常棒的工作,感謝您指點我的解決方案。 – donturner

2

在大多數情況下,你可以提高你的表現用JOIN代替子查詢(儘管這取決於很多因素左右)檢查查詢計劃。

PLUS: 化妝book_id一個FK到books.id 和(也許)省略代理鍵ID


CREATE TABLE book_genres 
     (book_id integer NOT NULL REFERENCES book (id) 
     , genre_id integer NOT NULL REFERENCES genre (id) 
     , PRIMARY KEY (book_id, genre_id) 
     ) ; 
CREATE INDEX ON book_genres (genre_id,book_id); 
+0

感謝,認爲:通過 「反向」 指標代替一列索引是我的第一次嘗試。不幸的是,如果本書既是體裁1又是體裁2,它會帶來重複。另外,增加「DISTINCT」會大大降低性能。 – donturner

+0

@donturner:我剛剛開始)。試試另一個^。關於第一個問題:你是否嘗試過「分組」? – potashin

+0

我試過GROUP BY,性能和DISTINCT一樣。不幸的是,您的新查詢需要大約3000毫秒才能執行。查詢計劃員輸出幫助? – donturner