Num*_*our 4 postgresql performance optimization view query-performance
使用 Postgres 9.4,我经常执行以下查询:
SELECT DISTINCT ON(recipient) * FROM messages
LEFT JOIN identities ON messages.recipient = identities.name
WHERE timestamp BETWEEN timeA AND timeB
ORDER BY recipient, timestamp DESC;
Run Code Online (Sandbox Code Playgroud)
所以我决定创建一个视图:
CREATE VIEW myView AS SELECT DISTINCT ON(recipient) * FROM messages
LEFT JOIN identities ON messages.recipient = identities.name
ORDER BY recipient, timestamp DESC;
Run Code Online (Sandbox Code Playgroud)
我刚刚意识到,如果我查询我的观点,就像SELECT * FROM myView WHERE timestamp BETWEEN timeA AND timeB我的表现要差很多一样。
这样EXPLAIN ANALYZE两个问题,我发现了原因是,在第二种情况下,数据库带来了所有记录,请问左连接,然后应用WHERE条款。换句话说,WHERE子句不会被下推到视图的查询中。我还尝试ORDER BY从视图中删除,但数据库仍然LEFT JOIN对完整数据而不是过滤集执行。
这种行为的原因是什么?有没有办法在使用视图时获得可比较的性能?
ype*_*eᵀᴹ 13
这个查询:
SELECT DISTINCT ON(recipient) * FROM messages
LEFT JOIN identities ON messages.recipient = identities.name
WHERE messages.timestamp BETWEEN timeA AND timeB
ORDER BY recipient, timestamp DESC;
Run Code Online (Sandbox Code Playgroud)
说:
对于timeA 和 timeB 之间的所有消息,找到收件人,并为每个收件人找到一条消息(timeA 和 timeB 之间的最新消息)。
这个查询(如果你使用视图,你会得到):
SELECT *
FROM
( SELECT DISTINCT ON(recipient) * FROM messages
LEFT JOIN identities ON messages.recipient = identities.name
ORDER BY recipient, timestamp DESC
) AS myView
WHERE timestamp BETWEEN timeA AND timeB;
Run Code Online (Sandbox Code Playgroud)
说:
对于所有消息,查找收件人,并为每个收件人查找一条消息(所有时间中的最新消息),然后仅显示时间 A 和时间 B 之间的消息。
因此,第一个查询将显示时间 A 和 B 之间的消息,而第二个查询不会显示(因为可能有一个或多个消息发送给同一收件人,晚于时间 B)。
因此,查询在逻辑上是不同的,并且不能(也不应该)为您的视图推送条件。
如果您想将参数传递给您的视图,请查看此问题中的两个答案(@a_horse_with_no_name和@Erwin Brandstetter),了解如何使用集合返回函数:将“WHERE”参数传递给 PostgreSQL 视图?
您可以创建这样的函数;
CREATE OR REPLACE FUNCTION public.get_messages_by_timestamp (
time_a timestamp,
time_b timestamp
)
RETURNS TABLE (
recipient varchar,
"timestamp" timestamp
) AS
$$
BEGIN
RETURN QUERY
SELECT DISTINCT ON (m.recipient)
m.recipient,
m."timestamp"
FROM messages m
LEFT JOIN identities i ON m.recipient = i.name
WHERE
m."timestamp" BETWEEN time_a AND time_b
ORDER BY
m.recipient,
m."timestamp" DESC;
END;
$$
LANGUAGE 'plpgsql';
Run Code Online (Sandbox Code Playgroud)
然后你可以像使用表格一样使用该功能
SELECT *
FROM get_messages_by_timestamp('2015-01-01', '2015-01-02')
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2898 次 |
| 最近记录: |