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

如何提高多对多SQL查询的性能?,sql,database,postgresql,join,schema,Sql,Database,Postgresql,Join,Schema,我认为书籍和体裁之间有一种多对多的关系。例如,《霍比特人》一书可能有“儿童”、“小说”和“幻想”等体裁 以下是模式: 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"), "ge

我认为书籍和体裁之间有一种多对多的关系。例如,《霍比特人》一书可能有“儿童”、“小说”和“幻想”等体裁

以下是模式:

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
  • 图书种类:80万册
  • 图书:200000
我正在尝试用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在内存中执行多个查询和操作

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


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

按价格+限额的订单可能是性能杀手:检查查询计划

加:用“反向”索引替换单列索引: 将book_id设置为books.id的FK 并且(可能)省略代理密钥id


按价格+限额的订单可能是性能杀手:检查查询计划

加:用“反向”索引替换单列索引: 将book_id设置为books.id的FK 并且(可能)省略代理密钥id



谢谢,那也是我第一次尝试。不幸的是,如果这本书同时属于体裁1和体裁2,它会带来重复。另外,添加
DISTINCT
会严重降低性能。@donturner:我刚刚开始)。请尝试另一个^。关于第一个:您是否尝试过按
分组
?我尝试过按
分组
,性能与
不同
相同。不幸的是,执行新查询大约需要3000毫秒。查询计划器输出会有帮助吗?谢谢,这也是我第一次尝试。不幸的是,如果这本书同时属于体裁1和体裁2,它会带来重复。另外,添加
DISTINCT
会严重降低性能。@donturner:我刚刚开始)。请尝试另一个^。关于第一个:您是否尝试过按
分组
?我尝试过按
分组
,性能与
不同
相同。不幸的是,执行新查询大约需要3000毫秒。查询计划器输出会有帮助吗?谢谢,我已经做了您建议的更改。性能与我当前的查询相同。需要注意的一点是,使用
OFFSET
(例如
OFFSET 500
)时,性能会逐渐变差。planner输出是否有帮助?(您在表修改后进行了真空分析?)删除
订单价格限制xxx
,性能可能会更好(如果没有太多行满足您的条件)偏移量会使情况变得更糟。“您进行了真空分析?”-这就是问题所在!!我没有运行那个。现在,当不使用
OFFSET
时,我的原始查询每次运行不到20ms,而当使用它时,每次运行不到200ms(这是可以接受的)。非常棒的工作,感谢您为我指出解决方案。您应该养成这样的习惯:在修改表内容(分布)或更改其结构(添加索引等)后运行
VACUUM分析_表以刷新统计数据。谢谢,我已经做了您建议的更改。性能与我当前的查询相同。需要注意的一点是,使用
OFFSET
(例如
OFFSET 500
)时,性能会逐渐变差。planner输出是否有帮助?(您在表修改后进行了真空分析?)删除
订单价格限制xxx
,性能可能会更好(如果没有太多行满足您的条件)偏移量会使情况变得更糟。“您进行了真空分析?”-这就是问题所在!!我没有运行那个。现在,当不使用
OFFSET
时,我的原始查询每次运行不到20ms,而当使用它时,每次运行不到200ms(这是可以接受的)。非常棒的工作,感谢您为我指出解决方案。您应该养成这样的习惯:在修改表内容(分布)或更改其结构(添加索引等)后运行
VACUUM分析_表以刷新统计信息。
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
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
        ;
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);