2008-10-08 3 views
2

SQL для поиска повторяющихся записей (в пределах группы)SQL для поиска повторяющихся записей (в пределах группы)

У меня есть небольшая проблема, и я не уверен, что будет лучшим способом, чтобы исправить это, как я имеют ограниченный доступ к самой базе данных (Oracle). В нашей таблице «СОБЫТИЕ» у нас есть около 160 тыс. Записей, каждый EVENT имеет GROUPID, а нормальная запись имеет ровно 5 строк с одним и тем же GROUPID. Из-за ошибки в настоящее время мы получаем пару дубликатов записей (дублируем, поэтому 10 строк вместо 5, просто разные EVENTID. Это может измениться, так что это всего лишь <> 5). Нам нужно отфильтровать все записи этих групп.

Из-за ограниченного доступа к базе данных мы не можем использовать временную таблицу, и мы не можем добавить индекс в столбец GROUPID, чтобы сделать его быстрее.

Мы можем получить GROUPIDs с этим запросом, но нам потребуется второй запрос, чтобы получить необходимые данные

select A."GROUPID" 
from "EVENT" A 
group by A."GROUPID" 
having count(A."GROUPID") <> 5 

Одним из решений было бы подвыбор:

select * 
from "EVENT" A 
where A."GROUPID" IN (
    select B."GROUPID" 
    from "EVENT" B 
    group by B."GROUPID" 
    having count(B."GROUPID") <> 5 
) 

Без индекса GROUPID и 160k записей, это занимает слишком много времени. Пробовал думать о соединении, которое может справиться с этим, но пока не может найти хорошего решения.

Любой может найти подходящее решение для этого, может быть?

Small edit: У нас нет 100% дубликатов здесь, так как каждая запись по-прежнему имеет уникальный идентификатор, а GROUPID также не является уникальным (поэтому нам нужно использовать «группу за») - или, может быть, я просто пропустите простое решение для этого :)

небольшой пример о данных (я не хочу, чтобы удалить его, просто найти его)

EVENTID | GROUPID | TYPEID
123456    123       12
123457    123       145
123458    123       2612
123459    123       41
123460    123       238

234567    123       12
234568    123       145
234569    123       2612
234570    123       41
234571    123       238

он имеет еще несколько столбцов, как метка времени и т.д., но, как вы можете видеть уже, все равно, кроме EVENTID.

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

+0

Можете ли вы привести пример группы с дубликатами и отметьте, какие строки должны быть удалены ? – Constantin 2008-10-08 13:08:00

ответ

5

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

select 
    a.* 
from 
    event as a 
inner join 
    (select groupid 
    from event 
    group by groupid 
    having count(*) <> 5) as b 
    on a.groupid = b.groupid 

Это довольно распространенный способ получения всей информации из строк в группа.

Как и ваш ответ и другие ответы, это будет работать намного быстрее с индексом на groupid. Это зависит от того, насколько DBA помогает сбалансировать преимущества вашего запроса намного быстрее, чем затраты на поддержание еще одного индекса.

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

2

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

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

+0

DBA не существует в течение недели, иначе это было бы самым простым способом, согласился. – FrankS 2008-10-08 12:59:31

+0

Вам не нужен индекс, чтобы сделать это быстрее - вам нужен другой запрос – 2008-10-08 14:24:37

2

С точки зрения SQL, я думаю, вы уже ответили на свой вопрос. Подход, который вы описали (т. Е. Использование подвыборки), прекрасен, и я был бы удивлен, если какой-либо другой способ написания запроса значительно отличался бы от производительности.

160K записей не очень много для меня. Я мог бы понять, если вы были недовольны работой этого запроса, если он входил в фрагмент кода приложения, но из его звуков вы просто используете его как часть некоторых упражнений по очистке данных. (и, следовательно, вы ожидаете, что вы будете более терпимы в плане производительности).

Даже без какого-либо вспомогательного индекса его еще две полные таблицы сканируют по 160 тыс. Строк, что, честно говоря, я ожидаю, что вы выполните какое-то неопределенное разумное время.

Обратитесь к администраторам db. Они помогли создать проблему, поэтому пусть они будут частью решения.

/EDIT/Тем временем запустите запрос, который у вас есть. Узнайте, сколько времени это займет, а не гадать. Еще лучше было бы запустить его, включив автотрассировку и опубликовать результаты здесь, и мы могли бы помочь вам немного его улучшить.

+0

Проблема в том, что недостающий индекс и администратор баз данных сейчас не существует, поэтому именно это не решение. Один только подзапрос занимает около 100 мс из-за недостающего индекса, умноженного на 160 тыс. Записей, что означает, что полный запрос занимает около 4-5 часов. Я боюсь, что на самом деле нет лучшего решения. – FrankS 2008-10-08 13:07:18

+0

Вы ошибаетесь. Это не займет 160K * 0,1 секунды, это займет больше 0,1 + 0,1 секунды! – 2008-10-08 13:13:58

4

Сколько времени занимает этот SQL? Вы только собираетесь запустить его, как я полагаю, исправив ошибку, которая вызвала коррупцию в первую очередь? Я только что установил тестовый пример:

SQL> create table my_objects as 
    2 select object_name, ceil(rownum/5) groupid, rpad('x',500,'x') filler 
    3 from all_objects; 

Table created. 

SQL> select count(*) from my_objects; 

    COUNT(*) 
---------- 
    83782 

SQL> select * from my_objects where groupid in (
    2 select groupid from my_objects 
    3 group by groupid 
    4 having count(*) <> 5 
    5 ); 

OBJECT_NAME      GROUPID FILLER 
------------------------------ ---------- -------------------------------- 
XYZ         16757 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
YYYY        16757 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 

Elapsed: 00:00:01.67 

Менее 2 секунд. Хорошо, мой стол имеет вдвое меньше, чем у вас, но 160K не огромен. Я добавил столбец filler, чтобы таблица заняла некоторое дисковое пространство. План выполнения AUTOTRACE был:

------------------------------------------------------------------------- 
| Id | Operation    | Name  | Rows | Bytes | Cost (%CPU)| 
------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT  |   | 389 | 112K| 14029 (2)| 
|* 1 | HASH JOIN   |   | 389 | 112K| 14029 (2)| 
| 2 | VIEW    | VW_NSO_1 | 94424 | 1198K| 6570 (2)| 
|* 3 | FILTER    |   |  |  |   | 
| 4 |  HASH GROUP BY  |   |  1 | 1198K| 6570 (2)| 
| 5 |  TABLE ACCESS FULL| MY_OBJECTS | 94424 | 1198K| 6504 (1)| 
| 6 | TABLE ACCESS FULL | MY_OBJECTS | 94424 | 25M| 6506 (1)| 
------------------------------------------------------------------------- 
0

Эта работа делает то, что вы хотите, и предлагает ли она лучшую производительность? (Я просто подумал, что брошу его в качестве предложения).

select * 
from group g 
where (select count(*) from event e where g.groupid = e.groupid) <> 5 
0

Как насчет аналитической:

SELECT * FROM (
SELECT eventid, groupid, typeid, COUNT(groupid) OVER (PARTITION BY groupid) group_count 
    FROM event 
) 
    WHERE group_count <> 5 
6

Классическим для аналитических запросов, чтобы решить:

select eventid, 
     groupid, 
     typeid 
from (
     Select eventid, 
       groupid, 
       typeid, 
       count(*) over (partition by group_id) count_by_group_id 
     from EVENT 
     ) 
where count_by_group_id <> 5 
Смежные вопросы