Sql 如何使用最有效的窗口功能在连接的值上构建聚合?
我在使用窗口函数在连接的值上构建聚合时遇到问题。看起来是这样的: 我得到了以下表格:Sql 如何使用最有效的窗口功能在连接的值上构建聚合?,sql,postgresql,Sql,Postgresql,我在使用窗口函数在连接的值上构建聚合时遇到问题。看起来是这样的: 我得到了以下表格: CREATE TABLE movies ( id SERIAL, name VARCHAR, year INT, genre VARCHAR, country VARCHAR ); CREATE TABLE tags ( id SERIAL, name VARCHAR ); CREATE TABLE movies_tags (
CREATE TABLE movies (
id SERIAL,
name VARCHAR,
year INT,
genre VARCHAR,
country VARCHAR
);
CREATE TABLE tags (
id SERIAL,
name VARCHAR
);
CREATE TABLE movies_tags (
id SERIAL,
movie_id INT,
tag_id INT
);
现在我想做以下陈述:
SELECT m.*, array_agg(t.name) AS tags
FROM movies m
LEFT JOIN movies_tags mt ON mt.movie_id = m.id
LEFT JOIN tags t ON t.id = mt.tag_id
ORDER BY m.name
LIMIT 10
由于select中的聚合,所有电影在从大加入中选择前10名之前都会加入所有标签。我的目标是,出于性能方面的考虑,只对前10部电影进行汇总。所以我做的是:
WITH top_movies AS (
SELECT m.*
FROM movies m
ORDER BY m.name
LIMIT 10
)
SELECT tm.*, array_agg(t.name) AS tags
FROM top_movies tm
LEFT JOIN movies_tags mt ON mt.movie_id = tm.id
LEFT JOIN tags t ON t.id = mt.tag_id
性能要好得多。但我还有一个问题。最终目标是创建一种形式的可重用组件,如Postgres中的函数或ORM(如Rails的活动记录)中的命名查询,我可以根据自己的需要动态修改这些组件,例如:
SELECT * FROM my_top_movies_with_tags() AS tm
WHERE tm.country = 'USA' AND tm.year <= 1995
LIMIT 10;
这给了我以后动态修改它的灵活性,但从性能角度看,这要糟糕得多
有没有其他我不知道的方法来实现我的目标
我的目标总结如下:
仅在有限的电影集而不是整个电影表上生成聚合数组_agg。
通过添加WHERE、ORDER和LIMIT语句保持可修改性。
表现很好。
用它来模拟极限怎么样
也请考虑在性能临界查询时使用CTE。
< P>可以将表单输入添加到临时表中,并使用该表进行筛选。 CREATE TEMP TABLE temp_inputs
(
country VARCHAR(80),
year int
)
ON COMMIT DELETE ROWS;
WITH top_movies AS (
SELECT m.*
FROM movies m
ORDER BY m.name
LIMIT 10
)
SELECT tm.*, array_agg(t.name) AS tags
FROM tmovies tm, temp_inputs
LEFT JOIN movies_tags mt ON mt.movie_id = tm.id
LEFT JOIN tags t ON t.id = mt.tag_id
and tm.country = temp_inputs.country AND tm.year <= temp_inputs.year
谢谢你的这篇文章!ruw_数的使用非常有趣。我将对它进行一些实验,并将其与我从其他方法获得的基准进行比较。感谢您的输入。但我不明白这是如何改进我的查询的。这种过滤可以使用常规的where语句来完成。或者它是否以某种方式修改了公共表表达式?通过使用临时表,参数的运行时值将嵌入到查询中,因此不需要动态搜索。您是否可以对此进行详细说明,或者给我一个链接,以获取更多信息?这听起来很有趣,但我不确定工作流程到底应该如何工作。如何以及何时将值输入临时表?我是否必须在主语句中自动使用的临时表中定期插入一行?据我所知,oncommitdelete行在事务结束时删除临时表的内容。所以我必须将语句包装到事务中,以确保临时表在开始时为空吗?我只找到下面的链接,它可以给出一个想法。我建议您检查您的查询的执行计划。不幸的是,没有过时的查询优化解决方案。如果在每次搜索后关闭连接,则无需检查临时表。
SELECT * FROM (
SELECT
m.*,
array_agg(t.name) AS tags,
row_number() OVER(ORDER BY m.name) AS rownum
FROM
movies m
LEFT JOIN movies_tags mt ON mt.movie_id = m.id
LEFT JOIN tags t ON t.id = mt.tag_id
--There're must be a GROUP BY here
) AS tmp
WHERE rownum <= 10;
CREATE TEMP TABLE temp_inputs
(
country VARCHAR(80),
year int
)
ON COMMIT DELETE ROWS;
WITH top_movies AS (
SELECT m.*
FROM movies m
ORDER BY m.name
LIMIT 10
)
SELECT tm.*, array_agg(t.name) AS tags
FROM tmovies tm, temp_inputs
LEFT JOIN movies_tags mt ON mt.movie_id = tm.id
LEFT JOIN tags t ON t.id = mt.tag_id
and tm.country = temp_inputs.country AND tm.year <= temp_inputs.year