过滤LEFT JOIN结果

时间:2009-08-10 17:26:44

标签: sql left-join

我有两张桌子:授权和和解。 “结算”包含授权的外键引用。

和解也可以具有状态(错误,接受等)。

鉴于此数据:

Authorizations           Settlements
id                id   |   auth_id   | status
-----             ---------------------------
1                  1         1          ERROR
2                  2         1          ACCEPTED

我正在尝试编写SQL查询以查找没有ACCEPTED结算记录的所有授权。我尝试过LEFT OUTER JOIN,但它返回太多行。例如:

SELECT * FROM authorizations a
LEFT OUTER JOIN settlements s ON a.id = s.auth_id
WHERE s.status is null OR s.status != 'ACCEPTED'

这样做的问题是,如果它有多个结算记录,它仍将返回授权记录,其中一个是已接受的。或者,如果有多个ERROR记录,则授权将返回两次。

如何才能获取单个授权记录,这些记录没有状态为“ACCEPTED”的相应结算记录?是否可以使用直接SQL,或者我是否必须在代码中过滤结果?

3 个答案:

答案 0 :(得分:7)

SELECT  *
FROM    authorizations a
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    Settlements s
        WHERE   s.auth_id = a.id
                AND s.status = 'ACCEPTED'
        )

答案 1 :(得分:2)

尝试

SELECT a.* FROM authorizations a
LEFT OUTER JOIN (SELECT S.* from settlements s1 
WHERE  s1.status = 'ACCEPTED')
ON a.id = s.auth_id
WHERE s.auth_id is null

这将挑选出所有被接受的记录,然后获取不属于该组的授权。

答案 2 :(得分:0)

根据您的示例,如果您将JOIN更改为RIGHT联接,则不需要检查s.status为null。

适用于SQL Server 2005+或Oracle 9i +:

WITH unacceptedSettlements AS (
     SELECT s.auth_id
       FROM SETTLEMENTS s
      WHERE s.status != 'ACCEPTED'
   GROUP BY s.auth_id)
SELECT t.*
  FROM AUTHORIZATIONS t
  JOIN unacceptedSettlements us ON us.auth_id = t.auth_id

任何数据库替代方案:

SELECT t.*
  FROM AUTHORIZATIONS t
  JOIN (SELECT s.auth_id 
         FROM SETTLEMENTS s
         WHERE s.status != 'ACCEPTED'
      GROUP BY s.auth_id) us ON us.auth_id = t.auth_id