У меня есть таблица 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"}            
1
amol desai 5 Сен 2019 в 20:57

2 ответа

Лучший ответ

Так как массив каждой строки независим, простой коррелированный подзапрос с конструктором 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 являются необязательными для повышения производительности. 1-й полностью избыточен. Каждое условие также исключает случай NULL. Второй подавляет все пустые обновления.

Видеть:

Если вы попытаетесь сделать это без сохранения исходного порядка, вы, вероятно, обновите большинство строк без необходимости, просто потому, что порядок или элементы изменились даже без дубликатов.

Требуется Postgres 9.4 или более поздняя версия.

db <> fiddle здесь

1
Erwin Brandstetter 5 Сен 2019 в 22:43

Я не утверждаю, что это эффективно, но что-то вроде этого может работать:

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
0
Hambone 5 Сен 2019 в 18:21