如何在没有 HAVING 子句的情况下改进此查询
How can I improve this query without HAVING Clause
有两个表:
- 用户
- 文件
users
:
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username TEXT NOT NULL UNIQUE
)
documents
:
CREATE TABLE IF NOT EXISTS documents (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users,
name TEXT NOT NULL,
value INT NOT NULL
)
我想按文档 name
和 value
过滤 users
。通常,过滤 2-5 个文档 name
和 value
之间。每个用户大约有 6-10 个文档。
我有一个庞大的数据库,想改进这个查询。我想我可以在没有 HAVING
子句的情况下获得更快的查询。非常感谢任何帮助。我使用 PostgreSQL 13.
我正在使用的查询:
SELECT
users.username,
jsonb_agg(jsonb_strip_nulls(jsonb_build_object('name', documents.name, 'value', documents.value))) AS docs
FROM
users
JOIN
documents
ON
users.id = documents.user_id
GROUP BY
users.username
HAVING
jsonb_agg(jsonb_build_object('name', documents.name, 'value', documents.value)) @? '$[*] ? (@.name == "doc1") ? (@.value == "2")'
对于大表,在最终过滤少数符合条件的行之前加入和聚合所有行非常昂贵。
首先过滤符合条件的文档,然后抓取同一用户的所有文档,聚合,最后加入用户应该快几个数量级:
SELECT u.username, d.docs
FROM (
SELECT user_id, jsonb_agg(jsonb_build_object('name', d.name, 'value', d.value)) AS docs
FROM documents d1
JOIN documents d USING (user_id)
WHERE d1.name = 'doc1'
AND d1.value = 2
-- AND d.name IS NOT NULL -- strip NULLs early
-- AND d.value IS NOT NULL -- if not defined NOT NULL anyway
GROUP BY 1
) d
JOIN users u ON u.id = d.user_id;
同时,我删除了 jsonb_strip_nulls()
,因为所有已处理的列都定义为 NOT NULL。还便宜。
可能会简化为 jsonb_build_object(d.name, d.value)
。
对于第一步,documents(name, value)
上的 索引 将帮助 很多.甚至可能在 documents(name, value, user_id)
上获得 index-only scans(取决于)。
假设 documents(user_id)
上也有一个索引应该是安全的。有助于下一步。同样,documents(user_id, name, value)
用于仅索引扫描。
最后,users(id)
上的索引。应该是给定的。同样,users(id, username)
用于仅索引扫描。
如果每个用户 (name, value)
不是 UNIQUE
(好像是这种情况),请改用 EXISTS
以避免重复:
SELECT u.username, d.docs
FROM (
SELECT user_id, jsonb_agg(jsonb_build_object('name', d.name, 'value', d.value)) AS docs
FROM documents d
WHERE EXISTS (
SELECT FROM documents d1
WHERE d1.user_id = d.user_id
AND d1.name = 'doc1'
AND d1.value = 2
)
GROUP BY 1
) d
JOIN users u ON u.id = d.user_id;
类似的查询计划,可以使用相同的索引。
相关:
Query with LEFT JOIN not returning rows for count of 0
Is a composite index also good for queries on the first field?
有两个表:
- 用户
- 文件
users
:
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username TEXT NOT NULL UNIQUE
)
documents
:
CREATE TABLE IF NOT EXISTS documents (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users,
name TEXT NOT NULL,
value INT NOT NULL
)
我想按文档 name
和 value
过滤 users
。通常,过滤 2-5 个文档 name
和 value
之间。每个用户大约有 6-10 个文档。
我有一个庞大的数据库,想改进这个查询。我想我可以在没有 HAVING
子句的情况下获得更快的查询。非常感谢任何帮助。我使用 PostgreSQL 13.
我正在使用的查询:
SELECT
users.username,
jsonb_agg(jsonb_strip_nulls(jsonb_build_object('name', documents.name, 'value', documents.value))) AS docs
FROM
users
JOIN
documents
ON
users.id = documents.user_id
GROUP BY
users.username
HAVING
jsonb_agg(jsonb_build_object('name', documents.name, 'value', documents.value)) @? '$[*] ? (@.name == "doc1") ? (@.value == "2")'
对于大表,在最终过滤少数符合条件的行之前加入和聚合所有行非常昂贵。
首先过滤符合条件的文档,然后抓取同一用户的所有文档,聚合,最后加入用户应该快几个数量级:
SELECT u.username, d.docs
FROM (
SELECT user_id, jsonb_agg(jsonb_build_object('name', d.name, 'value', d.value)) AS docs
FROM documents d1
JOIN documents d USING (user_id)
WHERE d1.name = 'doc1'
AND d1.value = 2
-- AND d.name IS NOT NULL -- strip NULLs early
-- AND d.value IS NOT NULL -- if not defined NOT NULL anyway
GROUP BY 1
) d
JOIN users u ON u.id = d.user_id;
同时,我删除了 jsonb_strip_nulls()
,因为所有已处理的列都定义为 NOT NULL。还便宜。
可能会简化为 jsonb_build_object(d.name, d.value)
。
对于第一步,documents(name, value)
上的 索引 将帮助 很多.甚至可能在 documents(name, value, user_id)
上获得 index-only scans(取决于)。
假设 documents(user_id)
上也有一个索引应该是安全的。有助于下一步。同样,documents(user_id, name, value)
用于仅索引扫描。
最后,users(id)
上的索引。应该是给定的。同样,users(id, username)
用于仅索引扫描。
如果每个用户 (name, value)
不是 UNIQUE
(好像是这种情况),请改用 EXISTS
以避免重复:
SELECT u.username, d.docs
FROM (
SELECT user_id, jsonb_agg(jsonb_build_object('name', d.name, 'value', d.value)) AS docs
FROM documents d
WHERE EXISTS (
SELECT FROM documents d1
WHERE d1.user_id = d.user_id
AND d1.name = 'doc1'
AND d1.value = 2
)
GROUP BY 1
) d
JOIN users u ON u.id = d.user_id;
类似的查询计划,可以使用相同的索引。
相关:
Query with LEFT JOIN not returning rows for count of 0
Is a composite index also good for queries on the first field?