用于查找重复条目的SQL(在组内)

时间:2008-10-08 12:46:47

标签: sql oracle join

查找重复条目的SQL(在组内)

我遇到了一个小问题,我不确定解决它的最佳方法是什么,因为我只能访问数据库(Oracle)本身。 在我们的表“EVENT”中,我们有大约160k个条目,每个EVENT都有一个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和160k条目的索引,则需要太长时间。 试着考虑一个可以解决这个问题的联接,但到目前为止找不到一个好的解决方案。

任何人都可以为此找到一个好的解决方案吗?

小编辑: 我们这里没有100%重复,因为每个条目仍然有一个唯一的ID,GROUPID也不是唯一的(这就是为什么我们需要使用“group by”) - 或者我可能只是错过了一个简单的解决方案:)

关于数据的小例子(我不想删除它,只是找到它)

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之外,一切都是相同的。

我们会经常运行它进行测试,找到错误并检查它是否再次发生。

7 个答案:

答案 0 :(得分: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

答案 1 :(得分: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来平衡使查询运行速度快得多的好处与维护另一个索引的成本之间的平衡。

如果DBA决定使用索引,请确保相应的人员了解其索引策略,而不是您编写的查询速度慢的方法。

答案 2 :(得分: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不是很大。我添加了填充列以使表占用一些磁盘空间。 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)|
-------------------------------------------------------------------------

答案 3 :(得分:2)

如果您的DBA不会添加索引以加快速度,请询问他们建议您做什么(毕竟这是他们付出的代价)。大概你有一个商业案例,为什么你需要这些信息,在这种情况下你的直接管理应该在你身边。

也许你可以要求你的DBA将数据复制到可以添加索引的数据库中。

答案 4 :(得分:2)

从SQL的角度来看,我认为你已经回答了自己的问题。你所描述的方法(即使用子选择)很好,如果编写查询的任何其他方式在性能方面存在很大差异,我会感到惊讶。

160K记录对我来说似乎不是很多。我能理解你是否对该查询的性能不满意,如果它进入了一段应用程序代码,但从它的声音中你只是将它用作一些数据清理练习的一部分。 (因此会期望你在表现方面更加宽容)。

即使没有任何支持索引,它仍然只有160K行的两个全表扫描,坦率地说,我希望在某种模糊的合理时间内执行。

与您的数据库管理员联系。他们帮助创造了问题,所以让他们成为解决方案的一部分。

/编辑/在此期间,运行您的查询。找出需要多长时间,而不是猜测。更好的方法是运行它,启用set autotrace,然后在此处发布结果,然后我们可以帮助您稍微改进它。

答案 5 :(得分:0)

这项工作是否符合您的要求,是否能提供更好的性能? (我以为我会把它作为建议扔进去。)

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

答案 6 :(得分:0)

分析如何:

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