聚合数组列中不同元素的单个数组,不包括 NULL
Posted
技术标签:
【中文标题】聚合数组列中不同元素的单个数组,不包括 NULL【英文标题】:Aggregate single array of distinct elements from array column, excluding NULL 【发布时间】:2021-10-10 21:52:21 【问题描述】:我正在尝试汇总存储在 PostgreSQL 9.6 数据库列中的时间戳的不同非空值。
所以给定一个包含以下内容的表格:
date_array
------------------------
2019-10-21 00:00:00.0
2019-08-06 00:00:00.0,2019-08-05 00:00:00.0
2019-08-05 00:00:00.0
(null)
2019-08-01 00:00:00.0,2019-08-06 00:00:00.0,null
期望的结果是:
2019-10-21 00:00:00.0, 2019-08-06 00:00:00.0, 2019-08-05 00:00:00.0, 2019-08-01 00:00:00.0
数组可以有不同的大小,所以我尝试过的大多数解决方案最终都会遇到代码 0:
SQL State: 2202E ERROR: cannot accumulate arrays of different dimensionality.
其他一些注意事项:
数组可以为空,数组可以包含空。它们恰好是日期的时间戳(例如,没有时间或时区)。但是在尝试简化问题时,我没有运气将示例数据更改为字符串(例如foo, bar, (null), foo,baz
) - 只是为了专注于问题并消除我错过/不了解的有关时间戳的任何问题 w/o时区。
以下 SQL 是我最接近的(它解决了除不同维度之外的所有问题):
SELECT
ARRAY_REMOVE ( ARRAY ( SELECT DISTINCT UNNEST ( ARRAY_AGG ( CASE WHEN ARRAY_NDIMS(example.date_array) > 0 AND example.date_array IS NOT NULL THEN example.date_array ELSE 'null' END ) ) ), NULL) as actualDates
FROM example;
我创建了以下 DB fiddle,其中包含示例数据,如果缺少上述内容,则说明问题:https://www.db-fiddle.com/f/8m469XTDmnt4iRkc5Si1eS/0
此外,我已经仔细阅读了有关该问题的 ***(以及 PostgreSQL 文档),并且有类似的问题和答案,但我发现没有一个问题能说明我遇到的相同问题。
【问题讨论】:
【参考方案1】:在FROM
子句中使用unnest()
(在横向连接中):
select array_agg(distinct elem order by elem desc) as result
from example
cross join unnest(date_array) as elem
where elem is not null
在DB Fiddle.中测试它
一般说明。使用数组构造函数的替代解决方案更有效,尤其是在上述简单的情况下。就个人而言,我更喜欢使用聚合函数,因为这种查询结构更通用、更灵活,易于扩展以处理更复杂的问题(例如,必须聚合多个列、按另一列分组等)。在这些不平凡的情况下,性能差异往往会减小,但使用聚合的代码仍然更干净、更易读。当您必须维护非常大型和复杂的项目时,这是一个极其重要的因素。
另见In Postgres select, return a column subquery as an array?
【讨论】:
【参考方案2】:Plain array_agg()
对数组执行此操作:
将所有输入数组连接成一个更高一级的数组 方面。 (输入必须具有相同的维度,并且 不能为空或 null。)
不是你需要的。见:
Is there something like a zip() function in PostgreSQL that combines two arrays?你需要这样的东西:unnest()
,对元素进行处理和排序,然后将结果集提供给ARRAY constructor:
SELECT ARRAY(
SELECT DISTINCT elem::date
FROM (SELECT unnest(date_array) FROM example) AS e(elem)
WHERE elem IS NOT NULL
ORDER BY elem DESC
);
db小提琴here
要明确:我们可以使用array_agg()
(采用非数组输入,与您的错误使用不同)而不是最终的 ARRAY 构造函数。但后者更快(也更简单,IMO)。
它们恰好是日期的时间戳(例如,没有时间或时区)
所以投射到date
并修剪噪音。
应该是最快的方式:
相关子查询比LATERAL
快一点(并且可以完成简单的工作)。
ARRAY 构造函数比聚合函数 array_agg()
快一点(并且可以完成简单的工作)。
最重要的是,在子查询中对DISTINCT
进行排序和应用通常比在聚合函数中的内联ORDER BY
和DISTINCT
更快(并且完成了简单的工作)。
见:
Unnest arrays of different dimensions
How to select 1d array from 2d array?
Why is array_agg() slower than the non-aggregate ARRAY() constructor?
What is the difference between LATERAL JOIN and a subquery in PostgreSQL?
性能对比:
db小提琴here
【讨论】:
array_agg ()
多年来一直被广泛用于任何非数组参数。带有数组参数的变体后来作为附加功能引入。委婉地说,您对此的看法有些夸张。
@klin 我添加了一个性能比较,为您的“奢侈感知”提供一些数字。
我在评论中没有提到性能。所以让我直截了当地说:答案的第一段是不真实的(或至少不完整)。没有标准和非标准array_agg()
。该函数通常用于聚合非数组值,并且还可以选择处理数组(以有限的方式)。我允许自己指出这一点,以免误导潜在的读者。
@klin:这里没有什么是不真实的。为了您的方便,我将“标准”一词替换为“普通”。准确地说,从 Postgres 9.5 开始,array_agg()
有两种不同的变体,一种采用非数组输入,一种采用数组输入。
是的,该函数有两种变体。您为什么要提供与问题无关的描述,这仍然是个谜。以上是关于聚合数组列中不同元素的单个数组,不包括 NULL的主要内容,如果未能解决你的问题,请参考以下文章