oracle ORA-01799: 列不能外连接到子查询

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14571254/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 04:48:41  来源:igfitidea点击:

ORA-01799: a column may not be outer-joined to a subquery

oracleoracle-sqldeveloper

提问by dotnet-practitioner

Here is my query

这是我的查询

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

and here is the error message that I am getting..

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

enter image description here

在此处输入图片说明

What am I doing wrong???

我究竟做错了什么???

回答by DazzaL

you can rewrite that by pushing the sub query so that its not outer joined:

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

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 ) 

回答by Dave Costa

Well, Oracle apparently doesn't support using a subquery inside the join condition for an outer join. So you need to get rid of the subquery.

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

The question is, why is it there at all? You have "<=" conditions in two places, so the predicate essentially says "all records whose effective date is no later than the latest effective date that is no later than now". If that's what you really want, you could simplify it to "all records whose effective date is no later than now", i.e.:

问题是,它为什么会在那里?你在两个地方有“<=”条件,所以谓词本质上说“所有有效日期不晚于不晚于现在的最新生效日期的记录”。如果这是您真正想要的,您可以将其简化为“生效日期不晚于现在的所有记录”,即:

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

Voila, no subquery.

瞧,没有子查询。

But is that really what you want, or did you mean that first "<=" to be just "=" -- i.e. find the record with the most recent effective date before now? If that's what you really want, it will be more complex to rewrite.

但这真的是你想要的,还是你的意思是第一个“<=”只是“=”——即找到最近生效日期之前的记录?如果那是你真正想要的,重写会更复杂。

回答by Aqib Butt

i also faced this issue today and comeup with

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

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

回答by rishabh agnihotri

Option 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;

Option 2

选项 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 works fine as long as you want to query MYCUSTOPTION table as of "today"(COI.EFFDT <= SYSDATE) which appears to be your req in this case. But it won't work if you want to query MYCUSTOPTION as of "C.SINCE_DT"

只要您想查询“今天”(COI.EFFDT <= SYSDATE)的 MYCUSTOPTION 表,Option1 就可以正常工作,这在这种情况下似乎是您的要求。但是如果你想从“C.SINCE_DT”查询 MYCUSTOPTION 就行不通了

Option2 looks bit complex but it works better, allows you to switch between sysdate or any other date field without changing anything else(COI.EFFDT <= C.SINCE_DT)

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

回答by LFLFM

Your question has already been answered, but someone might have a slightly different case where they need to get the latest EFFDT based on a column, instead of a fixed date. For those cases, I only found one IMPERFECT option, and one UGLY solution...

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

Imperfect option:

不完美的选择:

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

It is an imperfect option because if the CUST_OPT table has future dates, but no current (<=N.ISSUE_DT) dates, the outer join will not work and no rows will be returned. In general PeopleSoft terms (yes I saw your SETID+EFFDT there! ;-D) this wouldn't happen very often as people tend to create one 01/01/1900 EFFDT to make a first value effective since "forever", but since it's not always the case; we also have an ugly solution:

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

I also found one UGLY option (but I actually recommend it, and it solves the problem, so let's call it a solution), which is this:

我还发现了一个丑陋的选项(但我实际上推荐它,它解决了问题,所以我们称之为解决方案),它是这样的:

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

This option WILL return FUTURE rows which must be ignored! So we add the conditions on the SELECT statement that will IGNORE the returned values, if they weren't meant to be retrieved. Like I said... it's an UGLY solution, but it is a solution.

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

For my ugly solution, if the rows will be processed later in an Application Engine or PL/SQL or whatever; you can, instead of having a CASE statement for each column, just add a new column that will tell you that you fetched "improper" data and ignore the fields later in your code, based on this column, like this:

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