【问题标题】:Remove duplicate entries from string array column of postgres从 postgres 的字符串数组列中删除重复的条目
【发布时间】:2023-03-15 03:25:02
【问题描述】:

我有一个 PostgreSQL 表,其中有一列包含字符串数组。该行有一些唯一的数组字符串,或者一些也有重复的字符串。如果存在,我想从每行中删除重复的字符串。

我尝试了一些查询,但无法实现。

以下是表格:

  veh_id |             vehicle_types              
 --------+----------------------------------------
      1  | {"byd_tang","volt","viper","laferrari"} 
      2  | {"volt","viper"}                        
      3  | {"byd_tang","sonata","jaguarxf"}        
      4  | {"swift","teslax","mirai"}              
      5  | {"volt","viper"}                        
      6  | {"viper","ferrariff","bmwi8","viper"}   
      7  | {"ferrariff","viper","viper","volt"}    

我期待以下输出:

  veh_id |             vehicle_types              
 --------+----------------------------------------
      1  | {"byd_tang","volt","viper","laferrari"} 
      2  | {"volt","viper"}                        
      3  | {"byd_tang","sonata","jaguarxf"}        
      4  | {"swift","teslax","mirai"}              
      5  | {"volt","viper"}                        
      6  | {"viper","ferrariff","bmwi8"}           
      7  | {"ferrariff","viper","volt"}            

【问题讨论】:

    标签: sql arrays postgresql duplicates sql-update


    【解决方案1】:

    由于每一行的数组是独立的,一个带有 ARRAY 构造函数的普通相关子查询就可以完成这项工作:

    SELECT *, ARRAY(SELECT DISTINCT unnest (vehicle_types)) AS vehicle_types_uni
    FROM   vehicle;
    

    见:

    请注意,NULL 将转换为空数组 ('{}')。我们需要对它进行特殊处理,但无论如何它都被排除在下面的UPDATE 中。

    快速简单。但是不要使用这个。您没有这么说,但通常您希望保留数组元素的原始顺序。您的基本样本也表明了这一点。在相关子查询中使用WITH ORDINALITY,会变得更复杂一些:

    SELECT *, ARRAY (SELECT v
                     FROM   unnest(vehicle_types) WITH ORDINALITY t(v,ord)
                     GROUP  BY 1
                     ORDER  BY min(ord)
                    ) AS vehicle_types_uni
    FROM   vehicle;
    

    见:

    UPDATE 实际删除欺骗:

    UPDATE vehicle
    SET    vehicle_types = ARRAY (
                     SELECT v
                     FROM   unnest(vehicle_types) WITH ORDINALITY t(v,ord)
                     GROUP  BY 1
                     ORDER  BY min(ord)
                    )
    WHERE  cardinality(vehicle_types) > 1  -- optional
    AND    vehicle_types <> ARRAY (
                     SELECT v
                     FROM   unnest(vehicle_types) WITH ORDINALITY t(v,ord)
                     GROUP  BY 1
                     ORDER  BY min(ord)
                    ); -- suppress empty updates (optional)
    

    两个添加的WHERE 条件都是可选的以提高性能。第一个是完全多余的。每个条件也排除了NULL 的情况。第二个禁止所有空更新。

    见:

    如果您尝试在不保留原始顺序的情况下执行此操作,您可能会在不需要的情况下更新大多数行,因为即使没有重复,顺序或元素也会发生变化。

    需要 Postgres 9.4 或更高版本。

    db小提琴here

    【讨论】:

      【解决方案2】:

      我并没有声称它是有效的,但这样的事情可能会起作用:

      with expanded as (
        select veh_id, unnest (vehicle_types) as vehicle_type
        from vehicles
      )
      select veh_id, array_agg (distinct vehicle_type)
      from expanded
      group by veh_id
      

      如果你真的想花点心思做最坏情况 O(n) 的事情,你可以写一个自定义函数:

      create or replace function unique_array(input_array text[])
      returns text[] as $$
      DECLARE
        output_array text[];
        i integer;
      BEGIN
      
        output_array = array[]::text[];
      
        for i in 1..cardinality(input_array) loop
          if not (input_array[i] = any (output_array)) then
            output_array := output_array || input_array[i];
          end if;
        end loop;
      
        return output_array;
      END;
      $$
      language plpgsql
      

      使用示例:

      select veh_id, unique_array(vehicle_types)
      from vehicles
      

      【讨论】:

      • 为什么建议对函数进行如此糟糕的实现?使用在输入数组上使用 unnest 和 distinct 的普通 language sql 函数会更有效
      猜你喜欢
      • 1970-01-01
      • 2016-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多