SQL LEFT外连接只有一些来自右边的行?

时间:2009-08-24 12:15:15

标签: sql left-join

我有两个表TABLE_ATABLE_B,其中已加入的列为员工编号EMPNO

我想做一个正常的左外连接。但是,TABLE_B有一些软删除的记录(status='D'),我希望将其包括在内。只是为了澄清,TABLE_B可以有活动记录(status = null / a / anything)以及已删除的记录,在这种情况下,我不希望我的结果中有该员工。但是,如果TABLE_B中只有员工的删除记录,我希望员工被包含在结果中。我希望我的要求是明确的。 (我可以做一个冗长的qrslt类型的东西,得到我想要的东西,但我认为必须有一个更优化的方法,使用连接语法这样做)。非常感谢任何建议(即使没有加入)。他的新手正在尝试以下查询而没有预期的结果:

SELECT TABLE_A.EMPNO
FROM   TABLE_A
LEFT OUTER JOIN TABLE_B ON TABLE_A.EMPNO = TABLE_B.EMPNO AND TABLE_B.STATUS<>'D' 

非常感谢任何帮助。

5 个答案:

答案 0 :(得分:2)

只是澄清一下 - 除非表B中的行有其他而不是'D',否则应显示TABLE_A中的所有记录?

你需要在B上至少有一个非空列(我将以'B.ID'为例,这种方法应该有效):

SELECT TABLE_A.EMPNO
FROM TABLE_A
LEFT OUTER JOIN TABLE_B ON
  (TABLE_A.EMPNO = TABLE_B.EMPNO)
  AND (TABLE_B.STATUS <> 'D' OR TABLE_B.STATUS IS NULL)
WHERE
  TABLE_B.ID IS NULL

也就是说,颠倒你可能想到的逻辑 - 只有在你有排除 TABLE_A条目的行的情况下才加入TABLE_B,然后在末尾使用IS NULL来排除这些。这意味着只包括那些不匹配的那些(在TABLE_B中没有行,或者只有'D'行)。

替代方案可能是

SELECT TABLE_A.EMPNO
FROM TABLE_A
WHERE NOT EXISTS (
  SELECT * FROM TABLE_B 
  WHERE TABLE_B.EMPNO = TABLE_A.EMPNO
  AND (TABLE_B.STATUS <> 'D' OR TABLE_B.STATUS IS NULL)
)

答案 1 :(得分:0)

啊crud,这显然有效&gt;&lt;

SELECT TABLE_A.EMPNO
FROM   TABLE_A
LEFT OUTER JOIN TABLE_B ON TABLE_A.EMPNO = TABLE_B.EMPNO 
where TABLE_B.STATUS<>'D'

如果你们有任何额外的信息可以加入,请随意。

更新: 经过一段时间后看到这个问题并认为我会添加更多有用的信息:此链接有关于ANSI语法的良好信息 - http://www.oracle-base.com/articles/9i/ANSIISOSQLSupport.php

特别是链接页面中的这部分内容非常丰富:

  

可以将连接中的额外过滤条件添加到使用AND以形成复杂连接。当需要过滤条件来限制外部连接时,通常需要这些。如果这些过滤条件放在WHERE子句中,并且外部联接返回过滤器列的NULL值,则该行将被丢弃。如果过滤条件被编码为连接的一部分,则可以避免这种情况。

答案 2 :(得分:0)

以下查询将为您提供未删除的员工记录,或只有员工只有已删除的记录。

select
    a.*
from
    table_a a
    left join table_b b on
        a.empno = b.empno
where
    b.status <> 'D'
    or (b.status = 'D' and 
        (select count(distinct status) from table_b where empno = a.empno) = 1)

这是在ANSI SQL中,但如果我知道你的RDBMS,我可以提供一个更具体的解决方案。

答案 3 :(得分:0)

SELECT A.*, B.*
FROM
   Table_A A
   INNER JOIN Table_B B
      ON A.EmpNo = B.EmpNo
WHERE
   NOT EXISTS (
      SELECT *
      FROM Table_B X
      WHERE
          A.EmpNo = X.EmpNo
          AND X.Status <> 'D'
    )

我认为这样做了。不需要左连接,因为您只想包含所有(至少一个)已删除行的员工。

答案 4 :(得分:0)

这就是我理解这个问题的方法。您只需要包括满足以下任一条件的员工:

  • 员工在TABLE_B中只有(软)删除的行;

  • 员工在TABLE_B;

  • 中只有未删除的行
  • 员工在TABLE_B中根本没有行。

换句话说,如果员工在TABLE_B中同时包含已删除和未删除的行,请忽略该员工,否则请将其包括在内。

这就是我认为可以解决的问题:

SELECT DISTINCT a.EMPNO
FROM TABLE_A a
  LEFT JOIN TABLE_B b1 ON a.EMPNO = b1.EMPNO
  LEFT JOIN TABLE_B b2 ON b1.EMPNO = b2.EMPNO
    AND (b1.STATUS = 'D' AND (b2.STATUS <> 'D' OR b2 IS NULL) OR
         b2.STATUS = 'D' AND (b1.STATUS <> 'D' OR b1 IS NULL))
WHERE b2.EMPNO /* or whatever non-nullable column there is */ IS NULL

或者,您可以使用分组:

SELECT a.EMPNO
FROM TABLE_A a
  LEFT JOIN TABLE_B b ON a.EMPNO = b1.EMPNO
GROUP BY a.EMPNO
HAVING 0 IN (COUNT(CASE b.STATUS WHEN 'D' THEN 1 ELSE NULL END),
             COUNT(CASE b.STATUS WHEN 'D' THEN NULL ELSE 1 END))