Krz*_*iak 5 postgresql aggregate
如何告诉 PostgreSQL 返回第一个遇到的值而不是聚合列?
Table "public.cache"
Column | Type | Modifiers | Storage | Stats target | Description
-----------+---------+-----------+---------+--------------+-------------
user_id | integer | | plain | |
object_id | integer | | plain | |
data | integer | | plain | |
data2 | boolean | | plain | |
Indexes:
"cache_object_id_user_id_key" UNIQUE CONSTRAINT, btree (object_id, user_id)
"cache_user_id_object_id_key" UNIQUE CONSTRAINT, btree (user_id, object_id)
Has OIDs: no
Run Code Online (Sandbox Code Playgroud)
按 object_id 和 data2 进行查询分组将使哈希聚合,这是我想避免的。
SELECT object_id, data2 FROM cache GROUP BY object_id, data2;
Run Code Online (Sandbox Code Playgroud)
我发现bool_or()
但它会扫描坏情况下的所有值。
SELECT object_id, bool_or(data2) FROM cache GROUP BY object_id;
Run Code Online (Sandbox Code Playgroud)
而且,任何数据类型都没有这样的函数。我想要做的是从 data2 列获取任何值,以便引擎不必迭代所有行。
如果数据列是整数呢?
有多种方法可以做到这一点,具有不同的性能,具体取决于数据的分布(不同object_id
值的数量等)。
最容易编写的查询 - 但不一定是最有效的,当然是使用聚合,MIN()
或者MAX()
:
SELECT object_id, MIN(data2) AS data2 \nFROM cache \nGROUP BY object_id ;\n
Run Code Online (Sandbox Code Playgroud)\n\n如果你有一个索引,那么(object_id, data2)
在最近版本的 Postgres 中,这不会太糟糕,它可以使用仅索引扫描作为执行计划。
另一种方法是使用DISTINCT ON
语法。与上面相同的索引会有所帮助:
SELECT DISTINCT ON (object_id)\n object_id, data2 \nFROM cache \nORDER BY object_id ;\n
Run Code Online (Sandbox Code Playgroud)\n\n如果与表大小相比,值的数量较少object_id
,则不同的方法会更有效。假设您还有另一个表(例如objects
),object_id
其主键为:
SELECT o.object_id, c.data2 \nFROM objects AS o\n CROSS JOIN LATERAL \n ( SELECT data2\n FROM cache AS c \n WHERE c.object_id = o.object_id\n ORDER BY c.data2 \n LIMIT 1\n ) AS c ;\n
Run Code Online (Sandbox Code Playgroud)\n\n需要相同的索引。这ORDER BY
不是必需的,但有了索引,就不会影响效率。如果您没有objects
表,那么该部分必须替换为:
---\nFROM ( SELECT DISTINCT object_id FROM cache) AS o\n CROSS JOIN LATERAL \n---\n
Run Code Online (Sandbox Code Playgroud)\n\n但你会损失一些效率,尤其是在旧版本中。在这种情况下,您可以将此子查询替换为有效遍历索引的复杂递归查询object_id
。有关更多详细信息,请参阅 Posgres 文档:松散索引扫描。
另请阅读埃尔文在相关问题中的精彩回答:
\n\n\n\n最后但并非最不重要的一点是,出现问题的主要原因是:
\n\n\n\n\n我有非规范化的数据,我想避免......(我知道该组中的所有值都是相等的布尔值)。
\n
规范化表将导致更高效的查询。
\n小智 5
我已经多次遇到同样的问题,但 StackExchange 对我没有帮助。您可以创建用户定义的聚合函数。
此类函数将在组中的行上运行。至少您需要提供一个聚合函数。在我的例子中是func_first_value
.
下面的函数variadic argument type
适用于 Postgres 可以推导的任何类型。默认值为NULL
。我添加了前缀,以便在必要时可以轻松删除它们。
CREATE OR REPLACE FUNCTION func_first_value(v0 anyelement, v1 anyelement) RETURNS anyelement AS $$
BEGIN
IF v0 IS NOT NULL THEN
RETURN v0;
END IF;
RETURN v1;
END;
$$ LANGUAGE plpgsql;
-- test function
SELECT func_first_value('text'::VARCHAR, NULL), func_first_value(NULL::VARCHAR, NULL), func_first_value(NULL, 'text'::VARCHAR);
CREATE AGGREGATE agg_first_value (anyelement)
(
sfunc = func_first_value,
stype = anyelement
);
Run Code Online (Sandbox Code Playgroud)
在您的情况下,您需要查询:
SELECT object_id, agg_first_value(data2) FROM cache GROUP BY object_id;
Run Code Online (Sandbox Code Playgroud)
为了避免扫描所有值,需要提供SORTOP
或sort_operator
作为 Postgres 文档的名称。我没有找到任何如何使用它的示例。MIN
此外,编写自己的聚合函数比使用或MAX
内置 SQL 聚合慢很多(约 6 倍) 。
这是一种尝试:
SELECT distinct object_id
, first_value(data2) over (partition by object_id)
FROM cache
Run Code Online (Sandbox Code Playgroud)
first_value 几乎适用于任何类型。