2009-06-10 19 views
4

我在Oracle中有一个包含两列的表,我想查询包含值的唯一组合的记录,不管这些值的顺序如何。例如,如果我有下表:通过唯一一组列值过滤SQL查询,无论其顺序

create table RELATIONSHIPS (
    PERSON_1 number not null, 
    PERSON_2 number not null, 
    RELATIONSHIP number not null, 
    constraint PK_RELATIONSHIPS 
     primary key (PERSON_1, PERSON_2) 
); 

我想查询所有唯一关系。所以如果我有记录PERSON_1 = John和PERSON_2 = Jill,我不想看到PERSON_1 = Jill和PERSON_2 = John的另一个记录。

有没有简单的方法来做到这一点?

+0

你具体要允许表中的这种“重复”和查询时,只筛选出来?如果没有,那么你可能会考虑一个表格检查约束或插入/更新触发器,检查反转的主键不存在于表中。 – 2009-06-10 19:42:19

+0

是的,我想在表中允许重复的组合,但也需要找到一个检索唯一集的最佳方法。 – 2009-06-11 04:08:21

回答

1

对于是否想要阻止重复项被插入到数据库中存在一些不确定性。您可能只想获取唯一对,同时保留重复项。

因此,这里是后一种情况的替代解决方案,查询唯一对即使重复存在:

SELECT r1.* 
FROM Relationships r1 
LEFT OUTER JOIN Relationships r2 
    ON (r1.person_1 = r2.person_2 AND r1.person_2 = r2.person_1) 
WHERE r1.person_1 < r1.person_2 
    OR r2.person_1 IS NULL; 

所以,如果有匹配的行用id的逆转,有一个规则,以一个查询应更喜欢(id为数字顺序的人)。

如果没有匹配的行,那么r2将为NULL(这是外连接的工作方式),因此在这种情况下只需使用r1中的任何内容即可。

不需要使用GROUP BYDISTINCT,因为只能有零个或一个匹配的行。

在MySQL尝试,我得到了下面的优化方案:

+----+-------------+-------+--------+---------------+---------+---------+-----------------------------------+------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref        | rows | Extra     | 
+----+-------------+-------+--------+---------------+---------+---------+-----------------------------------+------+--------------------------+ 
| 1 | SIMPLE  | r1 | ALL | NULL   | NULL | NULL | NULL        | 2 |       | 
| 1 | SIMPLE  | r2 | eq_ref | PRIMARY  | PRIMARY | 8  | test.r1.person_2,test.r1.person_1 | 1 | Using where; Using index | 
+----+-------------+-------+--------+---------------+---------+---------+-----------------------------------+------+--------------------------+ 

这似乎是一个相当不错的使用索引。

3
select distinct 
case when PERSON_1>=PERSON_2 then PERSON_1 ELSE PERSON_2 END person_a, 
case when PERSON_1>=PERSON_2 then PERSON_2 ELSE PERSON_1 END person_b 
FROM RELATIONSHIPS; 
0

我觉得这样的事情应该做的伎俩:

select * from RELATIONSHIPS group by PERSON_1, PERSON_2 
4

是永远存在的两个方向上的关系?即如果John和Jill是相关的,那么是否有总是一个{John,Jill}和{Jill,John}?如果是这样,那么只限于Person_Person_2并采取不同的设置。

+0

我对我自己的解决方案的愚蠢感到印象深刻。 – tekBlues 2009-06-10 19:43:34

+0

这种关系并不总是存在于两个方向上。事实上,它很少存在于两个方向上。但是,这是可能的,所以我需要能够过滤出重复项。不幸的是,数据库已经在生产中,所以使用这种方法将不起作用,因为存在一些记录,使得Person_2 2009-06-11 04:12:41

+0

@tekBlues:没有这种“愚蠢的”解决方案。所有想法都欢迎。事实上,其中很多想法都非常有帮助,即使它们并不能解决我的具体问题。 – 2009-06-11 04:13:46

2

您应该在Relationships表上创建一个约束,以便数值person_1的值必须小于数值person_2的值。

create table RELATIONSHIPS (
    PERSON_1 number not null, 
    PERSON_2 number not null, 
    RELATIONSHIP number not null, 
    constraint PK_RELATIONSHIPS 
     primary key (PERSON_1, PERSON_2), 
    constraint UNIQ_RELATIONSHIPS 
     CHECK (PERSON_1 < PERSON_2) 
); 

这样你就可以肯定(2,1)永远不会被插入 - 它必须是(1,2)。那么你的PRIMARY KEY约束将会防止重复。 PS:我看到Marc Gravell比我有更快的回答,有类似的解决方案。

0

我觉得KM几乎没错,我加了concat。

SELECT DISTINCT * 
    FROM (SELECT DISTINCT concat(Person_1,Person_2) FROM RELATIONSHIPS 
      UNION 
      SELECT DISTINCT concat(Person_2, Person_1) FROM RELATIONSHIPS 
     ) dt 
0

它的缺憾赫克,但它会至少告诉你什么独特的组合,你有,只是没有在真正的方便的方式...

select distinct(case when person_1 <= person_2 then person_1||'|'||person_2 else person_2||'|'||person_1 end) 
from relationships; 
3

未经测试:

select least(person_1,person_2) 
    , greatest(person_1,person_2) 
    from relationships 
group by least(person_1,person_2) 
    , greatest(person_1,person_2) 

为防止出现这样的双重条目,可以使用相同的想法(测试!)添加唯一索引:

SQL> create table relationships 
    2 (person_1 number not null 
    3 , person_2 number not null 
    4 , relationship number not null 
    5 , constraint pk_relationships primary key (person_1, person_2) 
    6 ) 
    7/

Table created. 

SQL> create unique index ui_relationships on relationships(least(person_1,person_2),greatest(person_1,person_2)) 
    2/

Index created. 

SQL> insert into relationships values (1,2,0) 
    2/

1 row created. 

SQL> insert into relationships values (1,3,0) 
    2/

1 row created. 

SQL> insert into relationships values (2,1,0) 
    2/
insert into relationships values (2,1,0) 
* 
ERROR at line 1: 
ORA-00001: unique constraint (RWIJK.UI_RELATIONSHIPS) violated 

Regards, Rob。

0

可能最简单的解决方案(不需要更改数据结构或创建触发器)是创建一组没有重复条目的结果,并将其中一个重复条目添加到该集合中。

看起来是这样的:

select * from relationships where rowid not in 
    (select a.rowid from relationships a,relationships b 
     where a.person_1=b.person_2 and a.person_2=b.person_1) 
union all 
select * from relationships where rowid in 
    (select a.rowid from relationships a,relationships b where 
     a.person_1=b.person_2 and a.person_2=b.person_1 and a.person_1>a.person_2) 

但通常我从来没有一列主键创建表。

0

你可以只,

 
with rel as (
select *, 
     row_number() over (partition by least(person_1,person_2), 
             greatest(person_1,person_2)) as rn 
    from relationships 
     ) 
select * 
    from rel 
where rn = 1;