SQL Not Exists和子查询

时间:2015-02-23 19:36:06

标签: sql

我有这两张桌子:

  • Personid, name, address, age
  • KnowspersonA_id, personB_id

我希望返回比他们所知道的Person年龄超过5岁的Person的名字。

我正在尝试这个:

SELECT 
    P1.name
FROM 
    Persons P1, Persons P2, Knows K1
WHERE 
    P1.id = K1.personA_id
    AND P2.id = K1.personB_id
    AND NOT EXISTS (SELECT * 
                    FROM Persons P3, Persons P4, Knows K2
                    WHERE P3.id = K2.personA_id
                      AND P4.id = K2.personB_id 
                      AND (P3.age - P4.age) <= 5)

但它不起作用,我也不太清楚为什么。你能指出我正确的方向吗?提前致谢

4 个答案:

答案 0 :(得分:2)

对于每个人,找到他们的年龄和他们认识的每个人的年龄。然后计算他们认识的人的最大年龄并过滤掉:

SELECT P1.name
FROM Persons P1 JOIN
     Knows K1
     ON P1.id = K1.personA_id JOIN
     Persons P2 
     ON P2.id = K1.personB_id
GROUP BY P1.name, P1.age
HAVING ( p1.age - MAX(p2.age) ) > 5;

虽然您可以使用exists执行此操作,但我认为聚合是一种更简单的查询和更简单的逻辑。

答案 1 :(得分:1)

如果您只需要显示年龄差异> 5的关系

SELECT P1.name
FROM Persons P1
INNER JOIN  Knows K1 ON P1.id = K1.personA_id
INNER JOIN  Persons P2 ON P2.id = K1.personB_id
WHERE  (P1.age - P2.age)  > 5

如果你只需要让人们认为他们的所有关系至少有5年的年龄差异,那么@Gordon Linoff的答案就是你要找的

答案 2 :(得分:1)

如果你真的坚持存在,这可以解决问题:

SELECT p1.name AS the_older
     , p2.name AS the_younger
FROM Persons p1, Persons p2
WHERE p1.age - p2.age > 5 -- They are more than 5 years older
AND EXISTS (              -- than the one they know
    SELECT * 
    FROM Knows Kx
    WHERE Kx.personA_id = p1.id AND Kx.personB_id = p2.id
    );

可以使用JOIN语法完成类似的事情(年龄条件也可以移入连接条件):

SELECT p1.name AS the_older
     , p2.name AS the_younger
FROM Persons p1 
JOIN Persons p2 ON EXISTS (
    SELECT * FROM Knows Kx
    WHERE Kx.personA_id = p1.id AND Kx.personB_id = p2.id
    )
WHERE p1.age - p2.age > 5
;

更新:澄清问题后:

  • personA必须有一些朋友
  • 所有人都超过5岁

SELECT p1.name
FROM Persons p1
WHERE EXISTS ( -- must have friends
    SELECT * FROM Knows Kx
    WHERE Kx.personA_id = p1.id
    )
AND NOT EXISTS ( -- but no friends less than 5 years younger
    SELECT *
    FROM Knows Knx
    JOIN Persons p2 ON Knx.personB_id = p2.id
    WHERE Knx.personA_id = p1.id
    AND p1.age - p2.age <= 5
        );
    );

答案 3 :(得分:1)

起点是Knows表的结合:

SELECT    personA_id ,
          personB_id
FROM      @k k
UNION
SELECT    personB_id ,
          personA_id 
FROM      @k k

如果您只是从表格Knows的一侧进行验证,则会得到不正确的结果。

DECLARE @p TABLE
    (
      id INT ,
      name NVARCHAR(MAX) ,
      age INT
    )
DECLARE @k TABLE
    (
      personA_id INT ,
      personB_id INT
    )

INSERT  INTO @p
VALUES  ( 1, 'a', 10 ),
        ( 2, 'b', 14 ),
        ( 3, 'c', 30 ),
        ( 6, 'f', 35 ),
        ( 7, 'g', 45 )


INSERT  INTO @k
VALUES  ( 1, 2 ),
        ( 1, 3 ),
        ( 2, 3 ),
        ( 7, 6 )


SELECT  t.personA_id ,
        t.name
FROM    ( SELECT    personA_id ,
                    name ,
                    paage - pbage AS diff
          FROM      ( SELECT    personA_id ,
                                personB_id ,
                                pa.age paage ,
                                pb.age pbage ,
                                pa.name
                      FROM      @k k
                                JOIN @p pa ON pa.id = k.personA_id
                                JOIN @p pb ON pb.id = k.personB_id
                      UNION
                      SELECT    personB_id ,
                                personA_id ,
                                pb.age pbage ,
                                pa.age paage ,
                                pb.name
                      FROM      @k k
                                JOIN @p pa ON pa.id = k.personA_id
                                JOIN @p pb ON pb.id = k.personB_id
                    ) k
        ) t
GROUP BY t.personA_id ,
        t.name
HAVING  ( MIN(diff) > 5 )

输出:

personA_id  name
3           c
7           g

如果您将直接加入表Knows,那么您将获得:

SELECT  t.personA_id ,
        t.name
FROM    ( SELECT    personA_id ,
                    name ,
                    paage - pbage AS diff
          FROM      ( SELECT    personA_id ,
                                personB_id ,
                                pa.age paage ,
                                pb.age pbage ,
                                pa.name
                      FROM      @k k
                                JOIN @p pa ON pa.id = k.personA_id
                                JOIN @p pb ON pb.id = k.personB_id
                    ) k
        ) t
GROUP BY t.personA_id ,
        t.name
HAVING  ( MIN(diff) > 5 )

输出:

personA_id  name
7           g