Найти дубликат для комбинации строк — Oracle SQL

У меня есть таблица:

Таблица 1

unique_id       user_id   user_seq      col_name            value_val    position
1               100       1             test1               100          1
1               100       1             test2               123          1
1               100       1             test1               a            2
1               100       1             test2               text         2
1               100       1             test3               1Rw          2
1               100       1             test4               1Tes         2

2               101       1             test1               1            1
2               101       1             test2               1            1
2               101       1             test3               1            1
2               101       1             test4               1            1
2               101       1             test5               1            1

3               100       1             test1               100          1
3               100       1             test2               123          1
3               100       1             test1               a            2
3               100       1             test2               text         2
3               100       1             test3               1Rw          2
3               100       1             test4               1Tes         2

4               101       1             test1               1            1
4               101       1             test2               1            1
4               101       1             test3               1            1
4               101       1             test4               1            1

Мне нужно найти дубликат на основе следующего:

user_id, user_seq, col_name, value_val и position должны быть одинаковыми для разных unique_id.

В приведенном выше примере unique_id - 1 и 3 ТОЧНО одинаковы, поэтому они должны быть возвращены в качестве вывода.

Для unique_id = 2 и 4 разница test5 недоступна для unique_id = 4, поэтому она не будет зафиксирована.

Выход будет:

unique_id
1
3

Кроме того, мой набор данных огромен, около 50 миллионов записей, поэтому нужно оптимизированное решение. Любая помощь?

РЕДАКТИРОВАТЬ

Моя структура таблицы:

Name        Null? Type           
----------- ----- --------------         
UNIQUE_ID          NUMBER         
USER_SEQ           VARCHAR2(100)          
COL_NAME           VARCHAR2(263)  
VALUE_VAL          VARCHAR2(4000) 
POSITION           NUMBER             
USER_ID            NUMBER  

Нет доступных индексов.

ГРУППИРОВАТЬ ПО, ИМЕТЬ, СЧИТАТЬ (ОТЛИЧНЫЕ)

jarlh 28.05.2019 10:25

Я тоже об этом думал, но в моем случае это не сработает. Например user_id = 101 и user_seq = 1 дадут мне счет 2 для col_name = test1, тогда как он должен проверять комбинацию строк.

dang 28.05.2019 10:33
«Кроме того, мой набор данных огромен, около 50 миллионов записей, поэтому нужно оптимизированное решение. Любая помощь?» Также поделитесь структурами CREATE TABLE, чтобы мы знали типы данных и индексы.. Кроме того, 50 миллионов записей не являются «огромными», я должен был это сказать..
Raymond Nijland 28.05.2019 11:30

@RaymondNijland - у меня есть общий вывод desc table1; в вопросе. Сейчас индексов нет.

dang 28.05.2019 11:54

Также какую версию базы данных Oracle вы используете?

Raymond Nijland 28.05.2019 12:20

@RaymondNijland - я использую Oracle Enterprise 18c.

dang 28.05.2019 12:24

мне нужно поесть сейчас, но я бы сделал что-то вроде использования STANDARD_HASH() в сочетании с LISTAGG для создания контрольной суммы по группе вне записей, см. пример загрузить ее во временную таблицу (с индексами), и вы можете просто использовать синтаксис GROUP BY, HAVING, COUNT(DISTINCT ) во временной таблице ..

Raymond Nijland 28.05.2019 12:56

@RaymondNijland - выдает ошибку - ORA-01489: результат конкатенации строк слишком длинный

dang 28.05.2019 13:02
"выдает ошибку - ORA-01489: результат конкатенации строк слишком длинный" Я некоторое время не работал с базой данных Oracle, поэтому я заржавел, но это имело бы смысл в реальном наборе данных, и это VARCHAR2 , а если вместо этого использовать STANDARD_HASH(LISTAGG(TO_CLOB(hash))) ?
Raymond Nijland 28.05.2019 13:22

Это работает в первый раз, но если я прокручиваю вниз, выдает ошибку - ORA-01489: результат конкатенации строк слишком длинный

dang 28.05.2019 13:24

хорошо, попробуйте ALTER SYSTEM SET max_string_size=extended SCOPE= SPFILE;LISTAGG швы должны быть ограничены max_string_size настройкой ... Если это не сработает, считайте меня как iam, а затем отключите параметры, так как я некоторое время не занимался базой данных Oracle и я ржавый

Raymond Nijland 28.05.2019 13:28
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
11
78
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Если производительность не является проблемой, как насчет самостоятельного присоединения?

select a.unique_id as unique_id
from table1 a join table1 b
on a.user_id = b.user_id
and a.user_seq = b.user_seq
and a.col_name = b.col_name
and a.value_val = b.value_val
and a.position = b.position
and a.unique_id <> b.unique_id

Ах, в моей таблице более 50 миллионов записей. Есть ли оптимизированный способ сделать это?

dang 28.05.2019 11:02

Понимаю. Может тогда это поможет.select unique_id from ( select t.*, count(*) over (partition by [duplicate columns]) ct from table1 t) where ct > 1

CzarLazar 28.05.2019 11:19
Ответ принят как подходящий

Вот один из способов сделать это:

with sample_data as (select 1 unique_id, 100 user_id, 1 user_seq, 'test1' col_name, '100' value_val, 1 position from dual union all
                     select 1 unique_id, 100 user_id, 1 user_seq, 'test2' col_name, '123' value_val, 1 position from dual union all
                     select 1 unique_id, 100 user_id, 1 user_seq, 'test1' col_name, 'a' value_val, 2 position from dual union all
                     select 1 unique_id, 100 user_id, 1 user_seq, 'test2' col_name, 'text' value_val, 2 position from dual union all
                     select 1 unique_id, 100 user_id, 1 user_seq, 'test3' col_name, '1Rw' value_val, 2 position from dual union all
                     select 1 unique_id, 100 user_id, 1 user_seq, 'test4' col_name, '1Tes' value_val, 2 position from dual union all
                     select 2 unique_id, 101 user_id, 1 user_seq, 'test1' col_name, '1' value_val, 1 position from dual union all
                     select 2 unique_id, 101 user_id, 1 user_seq, 'test2' col_name, '1' value_val, 1 position from dual union all
                     select 2 unique_id, 101 user_id, 1 user_seq, 'test3' col_name, '1' value_val, 1 position from dual union all
                     select 2 unique_id, 101 user_id, 1 user_seq, 'test4' col_name, '1' value_val, 1 position from dual union all
                     select 2 unique_id, 101 user_id, 1 user_seq, 'test5' col_name, '1' value_val, 1 position from dual union all
                     select 3 unique_id, 100 user_id, 1 user_seq, 'test1' col_name, '100' value_val, 1 position from dual union all
                     select 3 unique_id, 100 user_id, 1 user_seq, 'test2' col_name, '123' value_val, 1 position from dual union all
                     select 3 unique_id, 100 user_id, 1 user_seq, 'test1' col_name, 'a' value_val, 2 position from dual union all
                     select 3 unique_id, 100 user_id, 1 user_seq, 'test2' col_name, 'text' value_val, 2 position from dual union all
                     select 3 unique_id, 100 user_id, 1 user_seq, 'test3' col_name, '1Rw' value_val, 2 position from dual union all
                     select 3 unique_id, 100 user_id, 1 user_seq, 'test4' col_name, '1Tes' value_val, 2 position from dual union all
                     select 4 unique_id, 101 user_id, 1 user_seq, 'test1' col_name, '1' value_val, 1 position from dual union all
                     select 4 unique_id, 101 user_id, 1 user_seq, 'test2' col_name, '1' value_val, 1 position from dual union all
                     select 4 unique_id, 101 user_id, 1 user_seq, 'test3' col_name, '1' value_val, 1 position from dual union all
                     select 4 unique_id, 101 user_id, 1 user_seq, 'test4' col_name, '1' value_val, 1 position from dual union all
                     select 6 unique_id, 101 user_id, 1 user_seq, 'test1' col_name, '1' value_val, 1 position from dual union all
                     select 6 unique_id, 101 user_id, 1 user_seq, 'test2' col_name, '1' value_val, 1 position from dual union all
                     select 6 unique_id, 101 user_id, 1 user_seq, 'test3' col_name, '1' value_val, 1 position from dual union all
                     select 6 unique_id, 101 user_id, 1 user_seq, 'test4' col_name, '1' value_val, 1 position from dual union all
                     select 7 unique_id, 101 user_id, 1 user_seq, 'test1' col_name, '1' value_val, 1 position from dual union all
                     select 7 unique_id, 101 user_id, 1 user_seq, 'test2' col_name, '1' value_val, 1 position from dual union all
                     select 7 unique_id, 101 user_id, 1 user_seq, 'test3' col_name, '1' value_val, 1 position from dual union all
                     select 7 unique_id, 101 user_id, 1 user_seq, 'test4' col_name, '1' value_val, 1 position from dual union all
                     select 5 unique_id, 100 user_id, 1 user_seq, 'test1' col_name, '100' value_val, 1 position from dual union all
                     select 5 unique_id, 100 user_id, 1 user_seq, 'test2' col_name, '123' value_val, 1 position from dual union all
                     select 5 unique_id, 100 user_id, 1 user_seq, 'test1' col_name, 'a' value_val, 2 position from dual union all
                     select 5 unique_id, 100 user_id, 1 user_seq, 'test2' col_name, 'text' value_val, 2 position from dual union all
                     select 5 unique_id, 100 user_id, 1 user_seq, 'test3' col_name, '1Rw' value_val, 2 position from dual union all
                     select 5 unique_id, 100 user_id, 1 user_seq, 'test4' col_name, '1Tes' value_val, 2 position from dual),
            cnts as (select unique_id,
                            user_id,
                            user_seq,
                            col_name,
                            value_val,
                            position,
                            count(*) over (partition by unique_id) cnt
                     from   sample_data),
             res as (select distinct sd1.unique_id id1,
                                     sd2.unique_id id2,
                                     sd1.cnt,
                                     count(*) over (partition by sd1.unique_id, sd2.unique_id) total_id1_rows_cnt
                     from   cnts sd1
                            inner join cnts sd2 on sd1.unique_id < sd2.unique_id
                                                   and sd1.user_id = sd2.user_id
                                                   and sd1.user_seq = sd2.user_seq
                                                   and sd1.col_name = sd2.col_name
                                                   and sd1.value_val = sd2.value_val
                                                   and sd1.position = sd2.position
                                                   and sd1.cnt = sd2.cnt)
select id1||','||listagg(id2, ',') within group (order by id2) grouped_unique_ids
from   res
where  id1 not in (select id2
                   from   res)
and    cnt = total_id1_rows_cnt
group by id1
order by grouped_unique_ids;

И вот рабочий пример db<>, чтобы доказать, что это работает

Должен ли я индексировать какие-либо конкретные столбцы, чтобы получить хорошую производительность с помощью этого SQL?

dang 28.05.2019 11:28

Я бы так не подумал, поскольку вы запрашиваете всю таблицу.

Boneist 28.05.2019 11:34
«Я бы так не подумал, поскольку вы запрашиваете всю таблицу». объединение, по крайней мере (в идеале), должно иметь несколько индексов столбцов, чтобы предотвратить план доступа вложенные циклы соединяются? правильно?
Raymond Nijland 28.05.2019 11:43
«Я работаю с Oracle SQL с 1999 года и полюбил его с тех пор, как кто-то показал мне, как соединить две таблицы вместе». как ты можешь забыть о присоединении, ты их любишь .. ладно, шутки в сторону..
Raymond Nijland 28.05.2019 11:47

@RaymondNijland, в отличие от компьютеров, объем памяти моего мозга тоже нельзя добавить! ?

Boneist 28.05.2019 11:48

Кроме того, группа по, похоже, не дает правильного вывода, я изменил 1 значение в unique_id = 5, это дало мне такой результат - dbfiddle.uk/…

dang 28.05.2019 12:00

@ты прав; группа не работает. Ре. индекс - вам нужно будет включить все столбцы в соединение.

Boneist 28.05.2019 12:13

@dang, версия с самоприсоединением тоже не работала, но мне удалось это исправить, так что она работает.

Boneist 28.05.2019 12:25

@Boneist - да, я собирался прокомментировать. Не могли бы вы поделиться dbfiddle?

dang 28.05.2019 12:26

Я видел обновленный файл dbfiddle. Выглядит многообещающе, но запрос довольно сложный. Нет ли более простого способа сделать это?

dang 28.05.2019 12:29

Предполагая, что вы можете объединить значения в строки, возможно, самый простой метод:

select *
from (select unique_id, count(*) over (partition by vals) as cnt
      from (select unique_id,
                   listagg(user_id || ':' || user_seq || ':' || col_name || ':' || value_val || ':' || position, ',') within group (order by user_id, user_seq, col_name, value_val, position) as vals
            from sample_data sd
            group by unique_id
           ) sd
      ) sd
where cnt > 1;

Здесь — это рабочий пример db<>.

Позвольте мне подчеркнуть: это не универсальное решение из-за внутренних ограничений длины строки в Oracle. Но это работает для ваших данных и может быть удобным решением вашей проблемы.

Вы правы, это не универсальное решение. Я получаю сообщение об ошибке - ORA-01489: слишком длинный результат конкатенации строк

dang 28.05.2019 14:03

@данг . . . Очень жаль. Это самое простое решение.

Gordon Linoff 28.05.2019 14:31

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