如何提高多对多SQL查询的性能?

时间:2014-05-15 11:35:17

标签: sql database postgresql join schema

我在书籍和流派之间有多对多的关系。例如"霍比特人"书可能有类型"孩子","小说"和#34;幻想"。

这是架构:

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:800,000
  • 书:200,000

我正在尝试在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),因此可以添加视图,索引或非规范化。我也在使用Django,因此可以使用Python / Django执行多个查询在内存中执行操作。

2 个答案:

答案 0 :(得分: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
        ;

按价格排序+ LIMIT可能是性能杀手:检查查询计划。

PLUS:将一列索引替换为"反转"指数: 把book_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);

答案 1 :(得分:2)

在大多数情况下,您可以使用JOIN而不是子查询来提高性能(虽然这取决于很多因素):

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
相关问题