我有下表:
CREATE TABLE person (
id INTEGER NOT NULL,
name TEXT,
CONSTRAINT person_pkey PRIMARY KEY(id)
);
INSERT INTO person ("id", "name")
VALUES
(1, E'Person1'),
(2, E'Person2'),
(3, E'Person3'),
(4, E'Person4'),
(5, E'Person5'),
(6, E'Person6');
CREATE TABLE person_book (
id INTEGER NOT NULL,
person_id INTEGER,
book_id INTEGER,
receive_date DATE,
expire_date DATE,
CONSTRAINT person_book_pkey PRIMARY KEY(id)
);
/* Data for the 'person_book' table (Records 1 - 9) */
INSERT INTO person_book ("id", "person_id", "book_id", "receive_date", "expire_date")
VALUES
(1, 1, 1, E'2016-01-18', NULL),
(2, 1, 2, E'2016-02-18', E'2016-10-18'),
(3, 1, 4, E'2016-03-18', E'2016-12-18'),
(4, 2, 3, E'2017-02-18', NULL),
(5, 3, 5, E'2015-02-18', E'2016-02-23'),
(6, 4, 34, E'2016-12-18', E'2018-02-18'),
(7, 5, 56, E'2016-12-28', NULL),
(8, 5, 34, E'2018-01-19', E'2018-10-09'),
(9, 5, 57, E'2018-06-09', E'2018-10-09');
CREATE TABLE book (
id INTEGER NOT NULL,
type TEXT,
CONSTRAINT book_pkey PRIMARY KEY(id)
) ;
/* Data for the 'book' table (Records 1 - 8) */
INSERT INTO book ("id", "type")
VALUES
( 1, E'Btype1'),
( 2, E'Btype2'),
( 3, E'Btype3'),
( 4, E'Btype4'),
( 5, E'Btype5'),
(34, E'Btype34'),
(56, E'Btype56'),
(67, E'Btype67');
我的查询应该列出所有人的姓名,对于最近收到的书籍类型为 (book_id IN (2, 4, 34, 56, 67)
) 的人,它应该显示书籍类型和到期日期;如果一个人没有收到这样的书类型,它应该显示空白作为书类型和到期日期。
我的查询是这样的:
SELECT p.name,
pb.expire_date,
b.type
FROM
(SELECT p.id AS person_id, MAX(pb.receive_date) recent_date
FROM
Person p
JOIN person_book pb ON pb.person_id = p.id
WHERE pb.book_id IN (2, 4, 34, 56, 67)
GROUP BY p.id
)tmp
JOIN person_book pb ON pb.person_id = tmp.person_id
AND tmp.recent_date = pb.receive_date AND pb.book_id IN
(2, 4, 34, 56, 67)
JOIN book b ON b.id = pb.book_id
RIGHT JOIN Person p ON p.id = pb.person_id
(正确的)结果:
name | expire_date | type
---------+-------------+---------
Person1 | 2016-12-18 | Btype4
Person2 | |
Person3 | |
Person4 | 2018-02-18 | Btype34
Person5 | 2018-10-09 | Btype34
Person6 | |
查询工作正常,但由于我正在将一个小表与一个大表连接起来,所以速度很慢。有什么有效的方法可以重写这个查询吗?
我本地的PostgreSQL版本是9.3.18;但查询应该也适用于版本 8.4,因为这是我们的生产版本。
最佳答案
你的设置有问题
My local PostgreSQL version is 9.3.18; but the query should work on version 8.4 as well since that's our productions version.
在查看查询之前,这会产生两个主要问题:
Postgres 8.4 太旧了。特别是对于“生产”。它已于 2014 年 7 月停产。不再进行安全升级,已经无可救药地过时了。紧急考虑升级到当前版本。
在开发和生产中使用截然不同的版本是一个装满子弹的步兵枪。未被发现的困惑和错误。我们在这里看到不止一个出于这种愚蠢行为的绝望请求。
更好的查询
这个等价物应该更简单和更快(也适用于 pg 8.4):
SELECT p.name, pb.expire_date, b.type
FROM (
SELECT DISTINCT ON (person_id)
person_id, book_id, expire_date
FROM person_book
WHERE book_id IN (2, 4, 34, 56, 67)
ORDER BY person_id, receive_date DESC NULLS LAST
) pb
JOIN book b ON b.id = pb.book_id
RIGHT JOIN person p ON p.id = pb.person_id;
为了优化读取性能,这个具有匹配排序顺序的部分多列索引将是完美的:
CREATE INDEX ON person_book (person_id, receive_date DESC NULLS LAST)
WHERE book_id IN (2, 4, 34, 56, 67);
在现代 Postgres 版本(9.2 或更高版本)中,您可以将 book_id, expire_date
附加到索引列以获得仅索引扫描。见:
关于 DISTINCT ON
:
关于 DESC NULLS LAST
:
关于postgresql - 查找每个组的最近日期并加入所有记录的性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48176932/