Oracle-多个表上的左外连接未返回所需的空值

时间:2016-05-11 14:50:25

标签: oracle join

我有3张桌子,CAL,SOURCE,HISTORY。

  

CAL TABLE CAL_DATE 01/05/16 02/05/16 03/05/16 04/05/16 05/05/16 06/05/16 07/05/16

     

SOURCE TABLE TABLE_ID GROUP 1210 Sales 1211 Reference 1230 Marketing 1245 Sales 1650 Reference 1784 Sales

     

HISTORY
RUN_DATE TABLE_ID STATUS 01/05/16 1210 COMPLETED 02/05/16 1210 COMPLETED 02/05/16 1211 COMPLETED 03/05/16 1211 COMPLETED 01/05/16 1230 COMPLETED 03/05/16 1230 COMPLETED

查询我使用过。

SELECT TO_CHAR(C.CAL_DATE,'mm/dd/yyyy') AS CAL_DATE,TO_CHAR(C.CAL_DATE,'day') AS WDAY,X.* FROM CAL C LEFT OUTER JOIN
(
    SELECT S.GROUP,S.TABLE_ID,H.RUN_DATE,H.STATUS FROM TABLE S 
    LEFT JOIN HISTORY H
    ON S.TABLE_ID=H.TABLE_ID
    WHERE 1=1 
    AND STATUS='COMPLETED'    
)  X
ON TO_CHAR(C.CAL_DATE,'dd/mm/yyyy')=TO_CHAR(RUN_DATE,'dd/mm/yyyy')
AND X.TABLE_ID IN (1210,1211,1230)
WHERE TO_CHAR(C.CAL_DATE,'mm/dd/yyyy') <= TO_CHAR('03/05/2016','mm/dd/yyyy')
ORDER BY SOURCE_TABLE_ID ASC 

预期输出如下,但我得到不同的输出。当我传递超过1个表时,incodition nulls被过滤掉。请帮我纠正查询。

  

CAL_DATE TABLEID Status 01/05/16 1210 Completed 02/05/16 1210 Completed 03/05/16 null null 01/05/16 null null 02/05/16 1211 Completed 03/05/16 1211 Completed 01/05/16 1230 Completed 02/05/16 null null 03/05/16 1230 Completed

1 个答案:

答案 0 :(得分:1)

您需要更好地了解LEFT JOIN的工作原理(外部连接一般 - 左/右和全[外]连接)

LEFT JOIN始终分两步执行:

SELECT ....
FROM table1
LEFT JOIN table1 ON join_conditions
WHERE where_conditions

步骤1 - 首先执行LEFT JOIN(使用ON子句中指定的条件连接两个表)
步骤2-WHERE条件应用于步骤1中的连接所产生的结果集

LEFT JOIN的工作原理 - 快速提醒:LEFT JOIN始终返回左表中的所有行,即使右表中没有匹配的行也是如此。如果没有匹配(ON条件的计算结果为false),则LEFT JOIN为右表返回NULL RIGHT JOIN以相同的方式工作,但它返回RIGHT表中的所有行,而不是左边的LEFT JOIN。

如果您有此查询,请执行以下操作:

SELECT S.GROUP,S.TABLE_ID,H.RUN_DATE,H.STATUS 
FROM source_table S 
LEFT JOIN HISTORY H
ON S.TABLE_ID=H.TABLE_ID
WHERE H.STATUS='COMPLETED'

数据库首先执行LEFT JOIN,即:

SELECT S.GROUP,S.TABLE_ID,H.RUN_DATE,H.STATUS 
FROM source_table S 
LEFT JOIN HISTORY H
ON S.TABLE_ID=H.TABLE_ID

上面的查询给出了以下结果(注意严格方面最后3行中的NULL):

|   S.GROUP | S.TABLE_ID |                 H.RUN_DATE |  H.STATUS |
|-----------|------------|----------------------------|-----------|
|     Sales |       1210 |  January, 05 2016 00:00:00 | COMPLETED |
|     Sales |       1210 | February, 05 2016 00:00:00 | COMPLETED |
| Reference |       1211 | February, 05 2016 00:00:00 | COMPLETED |
| Reference |       1211 |    March, 05 2016 00:00:00 | COMPLETED |
| Marketing |       1230 |  January, 05 2016 00:00:00 | COMPLETED |
| Marketing |       1230 |    March, 05 2016 00:00:00 | COMPLETED |
|     Sales |       1245 |                     (null) |    (null) |
| Reference |       1650 |                     (null) |    (null) |
|     Sales |       1784 |                     (null) |    (null) |

然后数据库对上面的结果集执行WHERE条件:

WHERE H.STATUS='COMPLETED'

由于NULL='COMPLETED'的计算结果为FALSE,因此查询的最终结果为:

|     GROUP | TABLE_ID |                   RUN_DATE |    STATUS |
|-----------|----------|----------------------------|-----------|
|     Sales |     1210 |  January, 05 2016 00:00:00 | COMPLETED |
|     Sales |     1210 | February, 05 2016 00:00:00 | COMPLETED |
| Reference |     1211 | February, 05 2016 00:00:00 | COMPLETED |
| Reference |     1211 |    March, 05 2016 00:00:00 | COMPLETED |
| Marketing |     1230 |  January, 05 2016 00:00:00 | COMPLETED |
| Marketing |     1230 |    March, 05 2016 00:00:00 | COMPLETED |

即:跳过所有NULL 请参阅此演示:http://sqlfiddle.com/#!9/e2ed0/3

如果您还要获取具有NULL值的记录,则需要将此条件更改为:

WHERE ( H.STATUS='COMPLETED' OR H.STATUS IS NULL )

您也可以从WHERE子句中删除该条件,并将其添加到LEFT JOIN的ON条件,即:

SELECT S.GROUP,S.TABLE_ID,H.RUN_DATE,H.STATUS 
FROM source_table S 
LEFT JOIN HISTORY H
ON ( S.TABLE_ID=H.TABLE_ID AND H.STATUS='COMPLETED' )

请参阅此演示中的最后一个查询:http://sqlfiddle.com/#!9/e2ed0/3