У меня есть таблица company с 60 столбцами. Цель состоит в том, чтобы создать инструмент для поиска, сравнения и устранения дубликатов в этой таблице.

Пример: я нашел 2 компании, которые потенциально являются одинаковыми, но мне нужно знать, какие значения (столбцы) отличаются в этих 2 строках, чтобы продолжить.

Я думаю, что можно сравнить столбец по столбцу x 60, но я ищу более простое и универсальное решение.

Что-то типа:

SELECT * FROM company where co_id=22
SHOW DIFFERENCE
SELECT * FROM company where co_id=33

В результате должны получиться разные имена столбцов.

0
sibert 20 Фев 2015 в 16:40

2 ответа

Лучший ответ

Для этого вы можете использовать промежуточное представление строк с ключом / значением, с функциями JSON или альтернативно с hstore (сейчас представляет только исторический интерес). JSON встроен во все достаточно свежие версии PostgreSQL, тогда как hstore должен быть установлен в базе данных с помощью CREATE EXTENSION.

Демо:

CREATE TABLE table1 (id int primary key, t1 text, t2 text, t3 text);

Давайте вставим две строки, которые отличаются первичным ключом, и еще один столбец (t3).

INSERT INTO table1 VALUES 
 (1,'foo','bar','baz'),
 (2,'foo','bar','biz');

Решение с json

Сначала получаем представление строк с исходным номером строки в виде ключа / значения, затем мы объединяем строки в пары на основе их исходных номеров строк и отфильтровываем строки с тем же столбцом «значение»

WITH rowcols AS (
  select rn,  key, value
  from (select row_number() over () as rn,
   row_to_json(table1.*) as r from table1) AS s
  cross join lateral json_each_text(s.r)
)
select r1.key from rowcols r1 join rowcols r2
on (r1.rn=r2.rn-1 and r1.key = r2.key)
where r1.value <> r2.value;

Результат образца:

key 
-----
 id
 t3

Решение с hstore

SELECT skeys(h1-h2) from 
  (select hstore(t.*) as h1 from table1 t where id=1) h1
 CROSS JOIN
  (select hstore(t.*) as h2 from table1 t where id=2) h2;

h1-h2 вычисляет разницу ключ за ключом, а skeys() выводит результат в виде набора.

Результат:

 skeys 
-------
 id
 t3

Список выбора может быть уточнен с помощью skeys((h1-h2)-'id'::text), чтобы всегда удалять id, который, как первичный ключ, очевидно, всегда будет различаться между строками.

5
Daniel Vérité 18 Июн 2020 в 14:36

Вот хранимая процедура, которая поможет вам разобраться ...

Хотя это должно работать «как есть», в нем нет проверки ошибок, которую вы должны добавить.

Он получает все столбцы в таблице и перебирает их. Разница в том, что количество отдельных элементов больше одного. Также вывод:

  • Подсчет количества отличий
  • Сообщения для каждого столбца, где есть разница

Было бы более полезно вернуть набор строк с различиями в столбцах. В любом случае, удачи!

Применение:

SELECT showdifference('public','company','co_id',22,33)


CREATE OR REPLACE FUNCTION showdifference(p_schema text, p_tablename text,p_idcolumn text,p_firstid integer, p_secondid integer)
  RETURNS INTEGER AS
$BODY$ 
DECLARE
    l_diffcount INTEGER;
    l_column text;
    l_dupcount integer;
    column_cursor CURSOR FOR select column_name from information_schema.columns where table_name = p_tablename and table_schema = p_schema and column_name <> p_idcolumn;
BEGIN


    -- need error checking here, to ensure the table and schema exist and the columns exist

    -- Should also check that the records ids exist.

    -- Should also check that the column type of the id field is integer


    -- Set the number of differences to zero.

    l_diffcount := 0;

    -- use a cursor to iterate over the columns found in information_schema.columns
    -- open the cursor

    OPEN column_cursor;

    LOOP
        FETCH column_cursor INTO l_column;
        EXIT WHEN NOT FOUND;

        -- build a query to see if there is a difference between the columns. If there is raise a notice
        EXECUTE 'select count(distinct  ' || quote_ident(l_column) || ' ) from ' || quote_ident(p_schema) || '.' || quote_ident(p_tablename) || ' where ' || quote_ident(p_idcolumn) || ' in ('|| p_firstid || ',' || p_secondid ||')'
        INTO l_dupcount;



        IF l_dupcount > 1 THEN
        -- increment the counter
        l_diffcount := l_diffcount +1;
        RAISE NOTICE  '% has % differences', l_column, l_dupcount ; -- for "real" you might want to return a rowset and could do something here

        END IF;


    END LOOP;




    -- close the cursor
    CLOSE column_cursor;


    RETURN l_diffcount;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE STRICT
  COST 100;
0
mlinth 20 Фев 2015 в 14:49