【问题标题】:Aggregate single array of distinct elements from array column, excluding NULL聚合数组列中不同元素的单个数组,不包括 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

此外,我已经仔细阅读了有关该问题的 stackoverflow(以及 PostgreSQL 文档),并且有类似的问题和答案,但我发现没有一个问题能说明我遇到的相同问题。

【问题讨论】:

    标签: sql arrays postgresql aggregation


    【解决方案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。)

      不是你需要的。见:

      你需要这样的东西: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 更快(并且完成了简单的工作)。

      见:

      性能对比:

      db小提琴here

      【讨论】:

      • array_agg () 多年来一直被广泛用于任何非数组参数。带有数组参数的变体后来作为附加功能引入。委婉地说,您对此的看法有些夸张。
      • @klin 我添加了一个性能比较,为您的“奢侈感知”提供一些数字。
      • 我在评论中没有提到性能。所以让我直截了当地说:答案的第一段是不真实的(或至少不完整)。没有标准非标准array_agg()。该函数通常用于聚合非数组值,并且还可以选择处理数组(以有限的方式)。我允许自己指出这一点,以免误导潜在的读者。
      • @klin:这里没有什么是不真实的。为了您的方便,我将“标准”一词替换为“普通”。准确地说,从 Postgres 9.5 开始,array_agg() 有两种不同的变体,一种采用非数组输入,一种采用数组输入。
      • 是的,该函数有两种变体。您为什么要提供与问题无关的描述,这仍然是个谜。
      猜你喜欢
      • 2022-10-13
      • 2011-12-07
      • 2022-01-25
      • 2019-05-25
      • 1970-01-01
      • 1970-01-01
      • 2016-09-25
      • 2015-11-14
      • 2013-02-02
      相关资源
      最近更新 更多