2013-01-28 12 views
15

Oto moje zapytanieORA-01799: kolumna nie może być zewnętrzna dołączył do podkwerendzie

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

i tutaj jest komunikat o błędzie, że jestem coraz ..

enter image description here

Co ja robię źle???

+0

Campus Solutions? Yukk! –

Odpowiedz

26

można przepisać, że przez popychanie zapytania podrzędnego tak że dołączył jej nie zewnętrzna:

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

Przeanalizowałem 'co' na końcu instrukcji wewnętrznej select (po sysdate), myląc go z "co" na początku instrukcji wewnętrznej select. W moim pytaniu, zrobiłem te różne, aby uniknąć tego zamieszania. Ale tego właśnie szukałem. – thursdaysgeek

2

Cóż, Oracle najwyraźniej nie obsługuje używania podzapytania wewnątrz warunku łączenia dla sprzężenia zewnętrznego. Musisz więc pozbyć się podkwerendy.

Pytanie brzmi, dlaczego w ogóle jest? Masz warunki "< =" w dwóch miejscach, więc predykat zasadniczo mówi "wszystkie rekordy, których data wejścia w życie ma nie później niż ostatnia data wejścia w życie, nie później niż teraz". Jeśli to, co naprawdę chcesz, można go uprościć, aby „wszystkie rekordy, których data wejścia w życie jest nie później niż teraz”, tj .:

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

voila, nie podzapytanie.

Ale czy naprawdę tego chcesz, czy też miałeś na myśli pierwsze "< =" być po prostu "=" - tj. Znaleźć rekord z najnowszą datą faktyczną przed teraz? Jeśli tego właśnie chcesz, zmiana będzie trudniejsza.

+0

Musimy pobrać rekord, który jest najnowszy w MYCUSTOPTION, dla tego cust_id ... –

+0

zaktualizował zapytanie i wyjął <= w pierwszej zewnętrznej klauzuli –

1

Twoje pytanie zostało już odpowiedział, ale ktoś może mieć nieco inny przypadek, w którym muszą uzyskać najnowsze EFFDT na podstawie kolumny, zamiast ustalonej daty. Dla tych przypadków, Znalazłem tylko jedną opcję niedoskonały i jedno rozwiązanie brzydki ...

opcja Imperfect:

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

Jest to opcja niedoskonała, ponieważ jeśli tabela CUST_OPT ma przyszłych dat, ale nie ma prądu (< = N.ISSUE_DT), połączenie zewnętrzne nie będzie działać i nie zostaną zwrócone żadne wiersze. Ogólnie rzecz biorąc, warunki PeopleSoft (tak, widziałem twój SETID + EFFDT tam! ;-D) nie zdarzało się to zbyt często, ponieważ ludzie mają tendencję do tworzenia jednego 01/01/1900 EFFDT, aby pierwsza wartość była skuteczna od "na zawsze", ale ponieważ nie zawsze tak jest; mamy również brzydkie rozwiązanie:

Znalazłam też jedną opcję brzydkie (ale tak naprawdę go polecam, i to rozwiązuje problem, więc nazwijmy to rozwiązanie), który brzmi następująco:

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

ten opcja WARTO zwróci wiersze FUTURE, które muszą zostać zignorowane! Dodajemy więc warunki do instrukcji SELECT, która Zignoruje zwracane wartości, jeśli nie były one przeznaczone do pobrania. Tak jak powiedziałem ... jest to rozwiązanie UGLY, ale jest rozwiązaniem.

Dla mojego brzydkiego rozwiązania, jeśli wiersze będą przetwarzane później w Silniku aplikacji lub PL/SQL lub czymkolwiek; Zamiast instrukcji CASE dla każdej kolumny możesz po prostu dodać nową kolumnę, która pokaże, że pobrano "nieprawidłowe dane" i zignorować pola później w kodzie, w oparciu o tę kolumnę, w następujący sposób:

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