Дубликат SQL с нулями - PullRequest
       3

Дубликат SQL с нулями

0 голосов
/ 25 октября 2018

По несколько безумным бизнес-причинам, связанным с получением довольно запутанных данных от нашего клиента, у меня есть следующая проблема:

1) У меня есть таблица с 6 полууникальными идентификаторами и одним автоматически увеличивающимся уникальным идентификатором.В таблице есть больше полей.Но они не важны для этого обсуждения.Не является точным типом данных, которые хранятся в полях.

2) Я хочу получить список уникальных идентификаторов всех строк, которые участвуют хотя бы в одной повторяющейся взаимосвязи.(Нет никакого дополнительного значения в идентификации всех пар строк, которые указывают на дублирование. Но, если решение предусматривает это, довольно просто получить набор дублированных строк. Так что это также будет хорошо)

3) Дубликат определяется как:

3a) Для каждого из этих 6 полей запись A должна либо соответствовать записи B, либо одно из них должно быть нулевым

3b) По крайней мере одно поледолжен точно совпадать (т. е. ни одно из них не равно нулю)

4) Все потенциальные дублирующие интересующие поля являются строками и не являются пустыми строками.Многие строки имеют по крайней мере одно из полей интереса как ноль, но (по крайней мере, если предположить, что наша логика загрузки работает) ни одна из них не может иметь более 3 из этих полей как ноль.

5) Точное соответствие содержимого строкиЭто хорошо.Нам не нужны какие-либо сопоставления, основанные на регулярных выражениях, без учета регистра ...

6) Фактические дубликаты в таблице довольно редки.

7) Мы работаем с PostgreSQL 9Допустимо использование специфичных для базы данных функций.

8) Таблица содержит 500 000 строк.Итак, наивный запрос, с которого я начал, представленный ниже, занимает слишком много времени, чтобы быть жизнеспособным.Предположительно, он работает в основном в экспоненциальном времени.В идеале результаты должны возвращаться менее чем за минуту при работе на сервере среднего уровня.

SELECT a.id
FROM myTable a
JOIN myTable b ON a.id < b.id
AND (a.field1 = b.field1 OR a.field1 IS NULL OR b.field1 IS NULL )
AND (a.field2 = b.field2 OR a.field2 IS NULL OR b.field2 IS NULL)
....
WHERE 
a.field1 = b.field1 OR a.field2 = b.field2 ...

9) Я также рассмотрел использование «group by».Но «group by» не считает две строки равными, если сгруппированный столбец в одном содержит значение NULL, а другой содержит значение.Если нет способа добиться такого поведения, group by не будет работать для моей логики "оба равны или, по крайней мере, один равен нулю".

10) Набор значений, которые можно ожидать в каждомМожно предположить, что строка не перекрывается с другими столбцами.то есть, кроме нуля, вы не ожидаете, что значение из поля 1 появится в каких-либо строках для поля 2.

Обновление: извините за отсутствие информации.Я предоставлю максимально приближенное представление о схеме таблицы.К сожалению, рассматриваемый проект находится в защите, и даже только имена полей таблицы могут раскрыть информацию об операционной безопасности.

CREATE TABLE a (
id serial NOT NULL PRIMARY KEY,
f1 character varying,
f2 character varying,
f3 character varying,
f4 character varying,
f5 character varying,
f6 character varying,
...Other columns that aren't really relevant
)

CREATE INDEX f1_idx
  ON public.a
  USING btree
  (f1 COLLATE pg_catalog."default");

...Same index for the other 5 fields.

Для удобства я скопирую вопрос Лоренце Альбе и отвечу на него здесь.

Если у вас есть три строки (1, 2, 3, 4, NULL, 6)

(1, 2, 3, NULL, 5, NULL)

(1, 2, 3, 4, 7, NULL)

, которые являются дубликатами?

(1, 2, 3, NULL, 5, NULL)

и

(1, 2, 3, 4, 7, NULL)

не являются дубликатами, потому что поле 5 не равно нулю в обоих, и они не равны.Два других являются дубликатами.

Я приведу еще несколько собственных примеров для ясности.(Просто для полноты я приведу примеры строк в виде строк. Но, как я уже сказал, их вхождение в строку не очень важно, поскольку нам требуются точные совпадения строк.

 ("1", "2", "3", "4", NULL, NULL)

И

 ("1","2","3",NULL,"9",NULL)

являются дубликатами, поскольку столбцы 4, 5 и 6 по меньшей мере равны нулю хотя бы в одном, а все остальные поля равны.

("1", "2", "3", "4", NULL, "6")

AND

("1","2","3",NULL,"9","7")

не равныдублирует, потому что поле 6 отличается, и ни одно не является нулевым

И два примера, более типичных для фактических данных;

(NULL, NULL, "3",   NULL, "5",  "6")

и

("1", "2",    NULL, "4",  NULL, "6")

являются дубликатами, потому что все поля, в которыхони отличаются, по крайней мере одна сторона равна нулю.

(NULL, NULL, "3",   NULL, "5",  "6")

и

("1", "2",    NULL, "4",  NULL, "6")

Да, это означает, что

(NULL, NULL, NULL, "4", "5", "6")

и

("1", "2", "3", NULL, NULL, NULL)

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

Другое обновление: я обновил пункт 2, чтобы отразить тот факт, что я хочу, чтобы все строки участвовали как минимумодна дублирующаяся пара.Таким образом, для трех строк (1, 2, 3, 4, NULL, 6)

(1, 2, 3, NULL, 5, NULL)

(1, 2, 3, 4, 7, NULL)

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

1 Ответ

0 голосов
/ 25 октября 2018

Используйте count() over(partition by ...), затем отфильтруйте результат для любых значений, превышающих 1:

CREATE TABLE mytable(
   ID   INTEGER  NOT NULL PRIMARY KEY 
  ,col1 VARCHAR(2) NOT NULL
  ,col2 VARCHAR(2)
  ,col3 VARCHAR(2) NOT NULL
  ,col4 VARCHAR(2)
  ,col5 VARCHAR(2)
  ,col6 VARCHAR(2)
);
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1001,'a1','b1','c1','d1','e1','f1');
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1002,'a1',NULL,'c1','d1','e1','f1');
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1003,'a1','b1','c1','d1','e1','f1');
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1004,'b1','c1','d1','e1','f1',NULL);
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1005,'a1','b1','c1',NULL,'e1','f1');
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1006,'b1','c1','d1','e1','f1',NULL);
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1007,'f1',NULL,'b1','c1','d1','e1');
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1008,'b1','c1','d1','e1','f1',NULL);
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1009,'c1','d1','e1','f1',NULL,NULL);
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1010,'c1','d1','e1','f1',NULL,'a1');
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1011,'a1','b1','c1','d1','e1','f1');
select
       *
     , count(*) over(partition by
                       coalesce(col1,'NULL')
                     , coalesce(col2,'NULL')
                     , coalesce(col3,'NULL')
                     , coalesce(col4,'NULL')
                     , coalesce(col5,'NULL')
                     , coalesce(col6,'NULL')
                     ) cv
from mytable
  id | col1 | col2 | col3 | col4 | col5 | col6 | cv
---: | :--- | :--- | :--- | :--- | :--- | :--- | -:
1001 | a1   | b1   | c1   | d1   | e1   | f1   |  3
1003 | a1   | b1   | c1   | d1   | e1   | f1   |  3
1011 | a1   | b1   | c1   | d1   | e1   | f1   |  3
1005 | a1   | b1   | c1   | <em>null</em> | e1   | f1   |  1
1002 | a1   | <em>null</em> | c1   | d1   | e1   | f1   |  1
1008 | b1   | c1   | d1   | e1   | f1   | <em>null</em> |  3
1004 | b1   | c1   | d1   | e1   | f1   | <em>null</em> |  3
1006 | b1   | c1   | d1   | e1   | f1   | <em>null</em> |  3
1010 | c1   | d1   | e1   | f1   | <em>null</em> | a1   |  1
1009 | c1   | d1   | e1   | f1   | <em>null</em> | <em>null</em> |  1
1007 | f1   | <em>null</em> | b1   | c1   | d1   | e1   |  1

Используйте вышеуказанный подход какподзапрос, а затем используйте where cv > 1, чтобы найти все строки, имеющие «дубликаты» в этих 6 столбцах.

db <> fiddle здесь


Пожалуйста, обратите внимание на силу наличия некоторых данных для работы.На самом деле, вы несете ответственность за предоставление данных примера с вашим вопросом (так как в любом случае вы уже владеете этими данными).НЕ пытайтесь объяснить одним словом, используйте данные для иллюстрации «как есть» и «быть», вы найдете свои вопросы легче подготовить и быстрее ответить.См. Почему я должен предоставить MCVE

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...