SQL自联接,不存在,还是其他什么?

时间:2014-06-05 12:19:56

标签: sql

多对多名称和角色表 -

create table t (name varchar, role varchar) ; 

insert into t (name, role) values ('joe', 'husband'), ('joe', 'father'),
    ('tom', 'husband'), ('neo', 'bachelor') ; 

> select * from t;
 name |   role   
------+----------
 joe  | husband
 joe  | father
 tom  | husband
 neo  | bachelor

需要转换为姓名和他所拥有的角色的映射 -

not_a    | name
---------+-----------
husband  | neo
father   | tom
father   | neo
bachelor | joe
bachelor | tom

如何在不迭代每个角色/名称的情况下在真正的SQL中实现这一点?

2 个答案:

答案 0 :(得分:2)

假设您只有此表,您可以使用:

SELECT  r.role AS not_a, n.Name
FROM    (SELECT DISTINCT Name FROM T) AS n
        CROSS JOIN (SELECT DISTINCT Role FROM T) AS r
WHERE   NOT EXISTS
        (   SELECT  1
            FROM    t 
            WHERE   t.Name = n.Name
            AND     t.Role = r.Role
        );

<强> Example on SQL Fiddle

主查询将生成所有名称/角色对,然后not exists将释放已存在的所有对。

如果您确实有名称和角色表,那么您可以使用实际表替换子查询:

SELECT  r.role AS not_a, n.Name
FROM    Names AS n
        CROSS JOIN Roles AS r
WHERE   NOT EXISTS
        (   SELECT  1
            FROM    t 
            WHERE   t.Name = n.Name
            AND     t.Role = r.Role
        );

您还没有指定DBMS,因此如果您使用的是MySQL,请使用LEFT JOIN\IS NULL will perform better than NOT EXISTS

SELECT  r.role AS not_a, n.Name
FROM    (SELECT DISTINCT Name FROM T) AS n
        CROSS JOIN (SELECT DISTINCT Role FROM T) AS r
        LEFT JOIN t
            ON t.Name = n.Name
            AND t.Role = r.Role
WHERE   t.Name IS NULL;

我也假设它只是一个演示,但在你的表DDL中你使用了VARCHAR而没有which is not a good idea at all

答案 1 :(得分:2)

获得某人没有的角色有点复杂。您必须生成所有名称和角色对,然后选择不存在的名称和角色。这使用左外连接。

以下是执行此操作的标准SQL:

select r.role as not_a, n.name
from (select distinct name from t) n cross join
     (select distinct role from t) r left outer join
     t
     on t.name = n.name and t.role = r.role
where t.name is null;

作为注释:从不在定义变量和列时使用varchar()而没有长度。默认值可能无法达到预期效果。