SQL для поиска повторяющихся записей (внутри группы) - PullRequest
3 голосов
/ 08 октября 2008

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

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

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

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

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 и 160 тыс. Записей это занимает слишком много времени. Пытался подумать о соединении, которое может справиться с этим, но пока не может найти хорошего решения.

Кто-нибудь может найти хорошее решение для этого, может быть?

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

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

EVENTID | GROUPID | TYPEID<br> 123456 123 12<br> 123457 123 145<br> 123458 123 2612<br> 123459 123 41<br> 123460 123 238<br> <br> 234567 123 12<br> 234568 123 145<br> 234569 123 2612<br> 234570 123 41<br> 234571 123 238<br>
У него есть еще несколько столбцов, таких как отметка времени и т. Д., Но, как вы уже можете видеть, все идентично, кроме EVENTID.

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

Ответы [ 7 ]

6 голосов
/ 08 октября 2008

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

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
5 голосов
/ 08 октября 2008

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

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. Администратор базы данных должен сбалансировать выгоду от выполнения вашего запроса намного быстрее с затратами на поддержку еще одного индекса.

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

4 голосов
/ 08 октября 2008

Сколько времени на самом деле занимает этот 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 секунд. ОК, моя таблица содержит в два раза меньше строк, чем ваша, но 160 Кб невелики. Я добавил столбец заполнителя, чтобы таблица занимала место на диске. План выполнения 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)|
-------------------------------------------------------------------------
2 голосов
/ 08 октября 2008

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

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

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

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

/ РЕДАКТИРОВАТЬ / Тем временем запустить запрос, который у вас есть. Узнайте, сколько времени это займет, а не гадать. Еще лучше было бы запустить его с включенным автоматическим отслеживанием и опубликовать результаты здесь, тогда мы могли бы помочь вам несколько улучшить его.

2 голосов
/ 08 октября 2008

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

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

0 голосов
/ 08 октября 2008

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

SELECT * FROM (
SELECT eventid, groupid, typeid, COUNT(groupid) OVER (PARTITION BY groupid) group_count
  FROM event
)
  WHERE group_count <> 5
0 голосов
/ 08 октября 2008

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

select * 
from group g
where (select count(*) from event e where g.groupid = e.groupid) <> 5
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...