ОБНОВЛЕНИЕ для удаления дубликатов из списка, разделенного запятыми

В моей базе данных Postgres у меня есть таблица с несколькими столбцами, где один столбец B имеет следующие значения:

A      B
a1     b0, b1, b1
a2     b2, b3
a3     b4, b4, b5, b5

Мой ожидаемый результат должен быть таким:

A      B
a1     b0, b1
a2     b2, b3
a3     b4, b5

Есть ли простой UPDATE в Postgres для этого?

Какой тип колонки B. Это текстовое поле, json или массив чего-то? Функция обновления существует, но зависит от типа B

Haleemur Ali 29.05.2019 03:10

вы не должны хранить значения, разделенные запятыми, в первую очередь. Это действительно плохой дизайн.

a_horse_with_no_name 29.05.2019 03:10

Любой такой вопрос потребности для предоставления фактического определения таблицы, показывающего типы данных и ограничения, а также используемую версию Postgres. Кроме того, почему избыточный формат b0, b1, b1 вместо просто 0, 1, 1? А почему не массив или нормализованный дизайн БД? И: складывать только последовательные дубликаты или все? Сохранить порядок элементов?

Erwin Brandstetter 29.05.2019 03:24
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
3
951
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Есть два способа добиться этого:

Оператор UPDATE, который распаковывает элементы и объединяет их обратно, удаляя дубликаты:

update bad_design
  set b = t.b
from (
  select a, string_agg(distinct trim(t.w), ',') b
  from bad_design
    cross join unnest(string_to_array(b, ',')) as t(w)
  group by a
) t
where t.a = bad_design.a;

Другой вариант, если вам нужно делать это очень часто, — создать функцию, удаляющую дубликаты, и использовать ее в ОБНОВЛЕНИИ:

create or replace function cleanup(p_list text)
  returns text
as
$$
  select string_agg(distinct trim(t.w), ',')
  from unnest(string_to_array(p_list, ',')) as t(w);
$$
language SQL;

Затем вы можете использовать это следующим образом:

update bad_design
  set b = cleanup(b);

Причина, по которой это так сложно, заключается в том, что вы неправильно нормализовали свою модель данных. С правильно нормализованной моделью вы могли бы просто создать индекс UNIQUE и предотвратить вставку дубликатов в первую очередь.

Предполагая: текущая версия Postgres, тип данных Btext, разделитель в вашем списке — ', ', удалите дубликаты все, сохраните исходный порядок элементов, большинство или все строки имеют дубликаты:

Тогда это должно быть самым простым и быстрым:

UPDATE tbl
SET    B = array_to_string( ARRAY(
         SELECT elem
         FROM   unnest(string_to_array(B, ', ')) WITH ORDINALITY x(elem, ord)
         GROUP  BY 1
         ORDER  BY min(ord)
         ), ', ');

Недостаток: строки все обновляются, даже если ничего не меняется. Если это затрагивает более нескольких строк, этого можно избежать...

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

Связанный:

Ответ принят как подходящий

Конкретный оператор обновления зависит от типа столбца b, но на самом деле существует только 3 различных способа хранения этих данных: в строке с разделителями, текстовом массиве или json.

Оператор обновления для текстового поля, разделенного запятыми, будет таким:

update mytable
set b = array_to_string(array(select distinct unnest(string_to_array(b, ', '))), ', ');

Если b является текстовым массивом, то:

update mytable
set b = array(select distinct unnest(b));

Если b является массивом json, то:

update mytable
set b = array_to_json(array(select distinct value from json_array_elements_text(b)));

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

Тем не менее, я бы также рекомендовал нормализовать ваши данные.

Эти приведенные выше операторы будут обновлять все строки в таблице, что приведет к более высокой стоимости выполнения. Я проиллюстрирую способ уменьшить количество обновлений, используя вариант текстового массива (поскольку для этого требуется самый короткий SQL-запрос):

update mytable
set b = array(select distinct unnest(b))
where array_length(b_array, 1) != (select count(distinct c) from unnest(b) c);

Другие вопросы по теме