聚合数组列中不同元素的单个数组,不包括 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 BYDISTINCT 更快(并且完成了简单的工作)。

见:

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的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Diesel 计算数组列中不同元素的数量?

如何在 spark sql 中对数组进行成员操作?

笔记

如何在单个数据表数组列中组合姓氏,名字中间名? -PHP

从猫鼬聚合管道中的数组数组中获取单个数组

MongoDB聚合:计算数组元素或距离之间的差异