2

我在选择一些已简化为以下示例的值时遇到问题。基本上,我有一个这样的表:

CREATE TABLE sample_table
(
  pk_id        NUMBER,
  business_id  NUMBER
)

现在该表中的一些business_id 是重复的,我需要知道这些记录的pk。

假设我(进一步)建立并填写表格,如下所示:

ALTER TABLE sample_table ADD (
  CONSTRAINT sample_table_PK
 PRIMARY KEY
 (pk_id));

 create sequence sample_sequence;

 create trigger sample_trigger before insert on sample_table for each row 
 begin
    :new.pk_id := sample_sequence.nextval; 
 end;


 insert into sample_table (business_id) values (1000);
 insert into sample_table (business_id) values (1001);
 insert into sample_table (business_id) values (1002);
 insert into sample_table (business_id) values (1003);
 insert into sample_table (business_id) values (1003);
 insert into sample_table (business_id) values (1004);

现在找出哪些business_id 是重复的很容易:

  SELECT   business_id, COUNT (business_id)
    FROM   sample_table
GROUP BY   business_id
  HAVING   COUNT (business_id) > 1;

但我不想要business_id,我想要pk_id。

我可以使用上述查询作为子查询来获取它们:

select * from sample_table where business_id in (
  SELECT   business_id
    FROM   sample_table
GROUP BY   business_id
  HAVING   COUNT (business_id) > 1);

或使用 COUNT ( * ) OVER PARTITION BY 和子查询分解

with q as 
(SELECT   business_id, COUNT ( * ) OVER (PARTITION BY business_id) totalcount
  FROM   sample_table)
select * from q
where q.totalcount > 1

但是它们都使我的查询非常慢(此示例的查询工作正常,但是当我处理大约 500.000 行的生产数据时,性能并不是那么好)所以我想知道是否有更好的方法来做到这一点。

4

2 回答 2

2

就表和 PK 索引而言,第一个查询:

SELECT * from sample_table where business_id in (
  SELECT   business_id
    FROM   sample_table
GROUP BY   business_id
  HAVING   COUNT (business_id) > 1);

将需要进行全表扫描以评估子查询,然后主查询还需要根据找到的 business_ids 列表进行全表扫描(PK 索引对此没有任何用处。)你会看到一个计划这样的事情:

-----------------------------------------------...
| Id  | Operation             | Name         | ...
-----------------------------------------------...
|   0 | SELECT STATEMENT      |              | ...
|*  1 |  HASH JOIN RIGHT SEMI |              | ...
|   2 |   VIEW                | VW_NSO_1     | ...
|*  3 |    FILTER             |              | ...
|   4 |     HASH GROUP BY     |              | ...
|   5 |      TABLE ACCESS FULL| SAMPLE_TABLE | ...
|   6 |   TABLE ACCESS FULL   | SAMPLE_TABLE | ...
-----------------------------------------------...

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("BUSINESS_ID"="BUSINESS_ID")
   3 - filter(COUNT(*)>1)

在 business_id 和 pk_id 上(按此顺序)抛出一个唯一索引,您应该能够放弃第二次表扫描并使用索引仅查找重复的 business_ids。(第一次表扫描是不可避免的,因为它必须检查所有行是否存在重复。)使用复合索引,Oracle 可以同时查找 business_id 并获取 pk_id,而无需跳回表。

-------------------------------------------------...
| Id  | Operation             | Name            |...
-------------------------------------------------...
|   0 | SELECT STATEMENT      |                 |...
|   1 |  NESTED LOOPS         |                 |...
|   2 |   VIEW                | VW_NSO_1        |...
|*  3 |    FILTER             |                 |...
|   4 |     HASH GROUP BY     |                 |...
|   5 |      TABLE ACCESS FULL| SAMPLE_TABLE    |...
|*  6 |   INDEX RANGE SCAN    | BUSINESS_ID_IDX |...
-------------------------------------------------...

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter(COUNT(*)>1)                          
   6 - access("BUSINESS_ID"="BUSINESS_ID")

如果重复是例外,这应该工作得很好。如果在最坏的情况下,所有 business_id 都是重复的,那么索引查找可能会变得很难看。

你可以尝试一些更时髦的东西:

SELECT business_id, LISTAGG(pk_id) WITHIN GROUP (ORDER BY pk_id)
FROM sample_table
GROUP BY business_id
HAVING COUNT(*) > 1

现在您只需进行一次全表扫描,但现在所有 pk_id 都在同一行上粘合在一起。

于 2013-08-08T16:23:02.120 回答
0

有几种方法可以做到这一点,我更喜欢使用 JOIN 因为这可以加快查询速度

SELECT   
  DISTINCT a.pk_id
FROM   
  sample_table a
  JOIN sample_table b ON ( a.pk_id <> b.pk_id AND a.business_id = b.business_id )

此外,关于business_idwolud 帮助的索引

于 2013-08-08T16:35:03.227 回答