ORA-01799:列可能不是外部连接到子查询

时间:2013-01-28 21:06:39

标签: oracle oracle-sqldeveloper

这是我的查询

SELECT 
    COUNT(C.SETID)
FROM 
    MYCUSTOMER C
    LEFT OUTER JOIN MYCUSTOPTION CO 
    ON 
        (C.SETID = CO.SETID 
            AND C.CUST_ID = CO.CUST_ID 
            AND CO.effdt = ( 
                SELECT MAX(COI.EFFDT) 
                FROM MYCUSTOPTION COI 
                WHERE 
                    COI.SETID = CO.SETID 
                                    AND COI.CUST_ID = CO.CUST_ID 
                                    AND COI.EFFDT <=SYSDATE    
                )
    )

这是我收到的错误消息..

enter image description here

我做错了什么???

6 个答案:

答案 0 :(得分:32)

你可以通过推送子查询来重写它,使其不在外部加入:

select Count(C.setid)
  from mycustomer C
       left outer join (select *
                          from mycustoption co
                         where co.effdt <= (select Max(COI.effdt)
                                              from mycustoption COI
                                             where COI.setid = co.setid
                                               and COI.cust_id = co.cust_id
                                               and COI.effdt <= sysdate)) co
                    on ( C.setid = CO.setid
                         and C.cust_id = CO.cust_id ) 

答案 1 :(得分:3)

好吧,Oracle显然不支持在连接条件中使用子查询进行外连接。所以你需要摆脱子查询。

问题是,它为什么会存在?您在两个地方都有“&lt; =”条件,因此谓词基本上表示“所有记录的生效日期不迟于不迟于现在的最新生效日期”。如果这是你真正想要的,你可以简化为“所有生效日期不晚于现在的记录”,即:

ON 
    (C.SETID = CO.SETID 
        AND C.CUST_ID = CO.CUST_ID 
        AND CO.effdt <= SYSDATE    
)

Voila,没有子查询。

但这真的是你想要的,还是你的意思是第一次“&lt; =”只是“=” - 即在现在之前找到最近生效日期的记录?如果那是你真正想要的,那么重写会更复杂。

答案 2 :(得分:1)

您的问题已经得到解答,但有些人可能会略有不同,他们需要根据专栏而不是固定日期获取最新的EFFDT。对于这些情况,我只发现了一个IMPERFECT选项和一个UGLY解决方案......

不完美的选项:

SELECT ...
FROM MYTABLE N, CUST_OPT C
WHERE  etc...
AND C.SETID           (+) = N.SETID
AND C.CUST_ID         (+) = N.CUST_ID
AND NVL(C.EFFDT,TO_DATE('01011900','DDMMYYYY')) = NVL((SELECT MAX(EFFDT)
                                                       FROM CUST_OPT SC
                                                       WHERE SC.SETID = C.SETID
                                                       AND   SC.CUST_ID = C.CUST_ID
                                                       AND   SC.EFFDT <= N.ISSUE_DT)
                                                       ,TO_DATE('01011900','DDMMYYYY'))

这是一个不完美的选项,因为如果CUST_OPT表具有将来的日期,但没有当前(&lt; = N.ISSUE_DT)日期,则外部联接将不起作用,并且不会返回任何行。一般来说PeopleSoft条款(是的,我在那里看到了你的SETID + EFFDT!;-D)这种情况不会经常发生,因为人们倾向于创建一个01/01/1900 EFFDT以使第一个值自“永远”起有效,但是情况并非总是如此;我们也有一个丑陋的解决方案:

我还找到了一个UGLY选项(但我实际上推荐它,它解决了问题,所以我们称之为解决方案),这就是:

SELECT n.field1, n.field2,
       CASE WHEN NVL(c.EFFDT,n.ISSUE_DT-1)<=n.ISSUE_DT THEN c.field1 ELSE NULL END,
       CASE WHEN NVL(c.EFFDT,n.ISSUE_DT-1)<=n.ISSUE_DT THEN c.field2 ELSE NULL END
FROM MYTABLE N, CUST_OPT C
WHERE  etc...
AND C.SETID           (+) = N.SETID
AND C.CUST_ID         (+) = N.CUST_ID
AND NVL(C.EFFDT,TO_DATE('01011900','DDMMYYYY')) = NVL((SELECT MAX(EFFDT)
                                                       FROM CUST_OPT SC
                                                       WHERE SC.SETID = C.SETID
                                                       AND   SC.CUST_ID = C.CUST_ID
                                                       AND   SC.EFFDT <= N.ISSUE_DT)
                                                     ,NVL( (SELECT MIN(EFFDT)
                                                            FROM CUST_OPT SC
                                                            WHERE SC.SETID = C.SETID
                                                            AND   SC.CUST_ID = C.CUST_ID
                                                            AND   SC.EFFDT >= N.ISSUE_DT)
                                                         ,TO_DATE('01011900','DDMMYYYY')
                                                         )
                                                     )

此选项将返回必须忽略的FUTURE行!因此,我们在SELECT语句中添加IGNORE返回值的条件(如果它们不是要检索的话)。 就像我说的......这是一个UGLY解决方案,但它是一个解决方案。

对于我丑陋的解决方案,如果稍后将在Application Engine或PL / SQL或其他任何行中处理这些行;您可以,而不是为每列添加CASE语句,只需添加一个新列,该列将告诉您提取“不正确”数据并忽略代码中的字段,基于此列,如下所示:

CASE WHEN NVL(c.EFFDT,n.ISSUE_DT-1)<=n.ISSUE_DT THEN 'N' ELSE 'Y' END AS IGNORE_CUST_OP_COLS

答案 3 :(得分:1)

我今天也遇到了这个问题,并提出了

SELECT 
COUNT(C.SETID)
FROM 
MYCUSTOMER C
LEFT OUTER JOIN MYCUSTOPTION CO 
ON 
    (C.SETID = CO.SETID 
        AND C.CUST_ID = CO.CUST_ID 
        AND CO.effdt IN ( 
            SELECT MAX(COI.EFFDT) 
            FROM MYCUSTOPTION COI 
            WHERE 
                COI.SETID = CO.SETID 
                                AND COI.CUST_ID = CO.CUST_ID 
                                AND COI.EFFDT <=SYSDATE    
            )
)

答案 4 :(得分:1)

选项1

select COUNT(C.SETID)
from MYCUSTOMER C
left outer join (
  select *
  from MYCUSTOPTION CO
    on CO.effdt = (
        select MAX(COI.EFFDT)
        from MYCUSTOPTION COI
        where COI.SETID = CO.SETID
          and COI.CUST_ID = CO.CUST_ID
          and COI.EFFDT <= SYSDATE
        )
  ) CO
  on C.SETID = CO.SETID
    and C.CUST_ID = CO.CUST_ID;

选项2

select COUNT(C.SETID)
from MYCUSTOMER C
left outer join MYCUSTOPTION CO
  on C.SETID = CO.SETID
    and C.CUST_ID = CO.CUST_ID
where nvl(CO.effdt, to_date('19000101', 'YYYYMMDD')) = NVL((
      select MAX(COI.EFFDT)
      from MYCUSTOPTION COI
      where COI.SETID = CO.SETID
        and COI.CUST_ID = CO.CUST_ID
        and COI.EFFDT <= C.SINCE_DT
      ), to_date('19000101', 'YYYYMMDD'))

Option1正常工作,只要您要查询“ today”(今天)(COI.EFFDT <= SYSDATE)时的MYCUSTOPTION表,在这种情况下,这就是您的要求。但是,如果您要从“ C.SINCE_DT”开始查询MYCUSTOPTION,将无法正常工作

Option2看起来有点复杂,但是效果更好,允许您在sysdate或任何其他日期字段之间进行切换,而无需更改其他任何内容(COI.EFFDT <= C.SINCE_DT)

答案 5 :(得分:0)

尝试了其他答案中的LEFT OUTER JOIN后,它可以工作,但速度确实很慢...

我个人最好的解决方案是创建与您需要的最新记录相关的VIEW,并对其进行左联接。

CREATE VIEW VIEW_SOF_TOF as
SELECT SOF."FIELDA1", TOF."FIELDB1", TOF."FIELDB2", ...
FROM SOF, TOF
WHERE FIELDA1 (+) = FIELDB1
  AND ( FIELDB2 = (SELECT MAX(FIELDB2) FROM TOF WHERE FIELDA1 = FIELDB1)
    OR FIELDB2 IS NULL)