30

这是我的查询

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

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

在此处输入图像描述

我究竟做错了什么???

4

6 回答 6

39

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

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 ) 
于 2013-01-28T22:05:24.223 回答
3

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

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

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

瞧,没有子查询。

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

于 2013-01-28T21:49:34.627 回答
2

选项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 <= SYSDATE) 开始查询 MYCUSTOPTION 表,Option1 就可以正常工作,在这种情况下,这似乎是您的要求。但是,如果您想从“C.SINCE_DT”开始查询 MYCUSTOPTION,它将不起作用

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

于 2019-11-19T22:55:39.887 回答
1

您的问题已经得到解答,但有些人的情况可能略有不同,他们需要根据列而不是固定日期获取最新的 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 表有未来日期,但没有当前 (<=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')
                                                         )
                                                     )

此选项将返回必须忽略的未来行!因此,我们在 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
于 2014-02-28T00:03:24.130 回答
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    
            )
)
于 2018-12-29T16:53:59.377 回答
0

在尝试LEFT OUTER JOIN了其他答案的那些之后,它可以工作,但它真的很慢......

就我而言,最好的解决方案是创建一个与您需要的最新记录相关的视图并对其执行左连接。

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)
于 2020-07-28T17:15:47.950 回答