如何基于一个特定值(而不是其他任何值)返回行?

时间:2018-08-31 22:26:43

标签: sql sql-server

假设我有2张桌子:

Person
+----------+---------+
| PersonID | Email   |
+----------+---------+
|        1 | a@a.com |
|        2 | b@b.com |
+----------+---------+

还有

PersonType
+--------------+----------+-------+
| PersonTypeID | PersonID | pType |
+--------------+----------+-------+
| 1            |        1 |     1 |
| 2            |        2 |     1 |
| 3            |        2 |     2 |
+--------------+----------+-------+

我想获得Person的行,其中pType等于1,而PersonType表中没有任何其他值。像这样

SELECT * 
FROM Person P 
INNER JOIN PersonType PT ON P.PersonID = PT.PersonID AND pType=1;

但是返回

+----------+---------+--------------+----------+-------+
| PersonID | Email   | PersonTypeID | PersonID | pType |
+----------+---------+--------------+----------+-------+
|        1 | a@a.com | 1            |        1 |     1 |
|        2 | b@b.com | 2            |        2 |     1 |
+----------+---------+--------------+----------+-------+

我希望它仅返回PersonID=1,因为PersonID=2的{​​{1}}为1和2。

pType

有可能吗?我尝试使用+----------+---------+--------------+----------+-------+ | PersonID | Email | PersonTypeID | PersonID | pType | +----------+---------+--------------+----------+-------+ | 1 | a@a.com | 1 | 1 | 1 | +----------+---------+--------------+----------+-------+ 等各种形式的联接,但似乎无法解决这个问题。

此外,除此以外,我想知道如何返回pType = 1 AND 2的行。

not IN

PersonID 2具有PType 1和2,因此我只想返回该Person。我也将如何做?谢谢。

编辑

使用Bohemian的答案,我想出了一些方法来解决为pType = 1 AND 2查找行的情况。

+----------+---------+--------------+----------+-------+
| PersonID | Email   | PersonTypeID | PersonID | pType |
+----------+---------+--------------+----------+-------+
|        2 | b@b.com | 2            |        2 |     1 |
|        2 | b@b.com | 3            |        2 |     2 |
+----------+---------+--------------+----------+-------+

这似乎可行。在这种情况下,有谁能做得更好?

3 个答案:

答案 0 :(得分:2)

将其他外部联接添加到其他类型,并过滤掉未匹配的子联接:

SELECT * 
FROM Person P 
JOIN PersonType PT ON P.PersonID = PT.PersonID AND PT.pType=1
LEFT JOIN PersonType PT2 ON P.PersonID = PT2.PersonID AND PT2.pType != 1
WHERE PT2.pType IS NULL —- this is only null when there are no hits for other types

您还可以使用更普通的不存在:

SELECT * 
FROM Person P 
JOIN PersonType PT ON P.PersonID = PT.PersonID AND PT.pType=1
WHERE NOT EXISTS (
    SELECT * FROM PersonType PT2
    WHERE P.PersonID = PT2.PersonID AND PT2.pType!=1)

但是它效率较低,因为它使用了相关子查询(尽管可以对其进行优化,但实践仍然很差)。

答案 1 :(得分:1)

如果您只想要人员而不是人员类型中的行,则可以使用existsnot exists

select p.*
from person p
where exists (select 1
              from persontype pt
              where pt.personid = p.personid and 
                    pt.ptype = 1 
             ) and
      not exists (select 1
                  from persontype pt
                  where pt.personid = p.personid and
                        pt.ptype <> 1
                 );

但是,更通用的解决方案将使用group byhaving。仅输入1:

select pt.personid
from persontype pt
group by pt.personid
having sum(case when pt.type = 1 then 1 else 0 end) > 0 and
       sum(case when pt.type <> 1 then 1 else 0 end) = 0;

对于类型1和2,没有其他类型:

select pt.personid
from persontype pt
group by pt.personid
having sum(case when pt.type = 1 then 1 else 0 end) > 0 and
       sum(case when pt.type = 2 then 1 else 0 end) > 0 and
       sum(case when pt.type not in (1, 2) then 1 else 0 end) = 0;

注意:其中第一个通常简化为:

select pt.personid
from persontype pt
group by pt.personid
having min(pt.type) = max(pt.type) and
       min(pt.type) = 1;

答案 2 :(得分:1)

您可以尝试使用NOT EXISTS代替LEFT JOIN

SELECT *
FROM Person p
JOIN PersonType pt ON p.PersonID = pt.PersonID
LEFT JOIN (
  SELECT PersonID
  FROM PersonType
  WHERE
      PType > 1
) v ON v.PersonID = p.PersonID
WHERE
  pt.PType = 1
  AND v.PersonID IS NULL