如何提高多对多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
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);