【问题标题】:ORA-01799: a column may not be outer-joined to a subqueryORA-01799: 列不能外连接到子查询
【发布时间】:2013-01-12 08:03:23
【问题描述】:

这是我的查询

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    
                )
    )

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

我做错了什么???

【问题讨论】:

    标签: oracle oracle-sqldeveloper


    【解决方案1】:

    您可以通过推送子查询来重写它,使其不外联:

    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 ) 
    

    【讨论】:

    • 我忽略了内部 select 语句末尾的“co”(在 sysdate 之后),将它与内部 select 语句开头的“co”混淆了。在我的查询中,我做了不同的处理,以避免这种混淆。但是,这正是我想要的。
    【解决方案2】:

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

    问题是,它为什么会存在?您在两个地方有“

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

    瞧,没有子查询。

    但这真的是你想要的,还是你的意思是第一个“

    【讨论】:

    • 我们需要为该 cust_id 获取 MYCUSTOPTION 中最新的记录...
    【解决方案3】:

    选项 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'))
    

    只要您想从“今天”(COI.EFFDT

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

    【讨论】:

      【解决方案4】:

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

      不完美的选择:

      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 表有未来日期,但没有当前 (

      我还找到了一个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')
                                                               )
                                                           )
      

      此选项将返回必须忽略的未来行!因此,我们在 SELECT 语句中添加条件,如果它们不打算被检索,则将 IGNORE 返回值。 就像我说的......这是一个丑陋的解决方案,但它是一个解决方案。

      对于我丑陋的解决方案,如果这些行稍后将在应用程序引擎或 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
      

      【讨论】:

        【解决方案5】:

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

        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    
                    )
        )
        

        【讨论】:

        • 您的更改是在第 9 行将“=”改为“IN”,但对我不起作用!
        【解决方案6】:

        在尝试了其他答案中的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)
        

        【讨论】:

          猜你喜欢
          • 2016-12-17
          • 1970-01-01
          • 2012-12-04
          • 2016-06-04
          • 2015-09-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多