2016-03-29 13 views
0

Ich erstelle eine sehr komplexe SQL-Abfrage in Oracle mit mehreren CTEs, wobei letztere Ausdrücke auf Werten von früheren basieren. Ich finde jedoch, dass die gesamte Ausführung anhält, wenn einer der vorherigen CTEs keine Daten enthält. Zum Beispiel:Oracle SQL CTE (allgemeiner Tabellenausdruck) wo keine Daten

WITH CTE1 AS 
    (
    SELECT 
     PEOPLE.ID AS PID, 
     APPLICATIONS.DATE AS APPDATE 
    FROM 
     PEOPLE, 
     APPLICATIONS 
    WHERE 
     APPLICATIONS.PERSON_ID = PEOPLE.ID 
     AND APPLICATIONS.DATE BETWEEN TO_DATE('2015-01-01','YYYY-MM-DD') AND TO_DATE('2015-01-31','YYYY-MM-DD') 
    ), 
CTE2 AS 
    (
    SELECT 
     APPLICATIONS.PERSON_ID AS PID 
     MIN(APPLICATIONS.DATE) AS EARLIEST_APPDATE 
    FROM 
     CTE1, 
     APPLICATIONS 
    WHERE 
     APPLICATIONS.PERSON_ID = CTE1.PID 
     AND APPLICATIONS.DATE < ADD_MONTHS(CTE1.APPDATE, -18) 
    GROUP BY APPLICATIONS.PERSON_ID 
    ), 
MAIN_QUERY AS 
    (
    SELECT 
     CTE1.PID AS PID 
    FROM 
     CTE1, CTE2 
    WHERE 
     -- Note that the PIDs should either match, or should not exist in CTE2 
     CTE1.PID = CTE2.PID OR (NOT EXISTS (SELECT PID FROM CTE2 WHERE CTE1.PID = CTE2.PID)) 
    ) 
SELECT 
    MAIN_QUERY.PID 
FROM MAIN_QUERY 

Natürlich weiß ich, dass das obige Beispiel in sich völlig sinnlos ist, aber ich habe das nur vereinfacht das Problem zu veranschaulichen. CTE2 gibt das früheste Datum einer Anwendung zurück, die von derselben Personenkennung erstellt wurde, bei der die Anmeldung mehr als 18 Monate vor dem Anmeldetag von CTE1 liegt. Wie auch immer ... was ist, wenn es solche Anwendungen nicht gibt? CTE2 kann null Zeilen zurückgeben.

Sie werden feststellen, dass CTE2 nicht selbst in der endgültigen Abfrage referenziert wird. Ein leerer CTE2 wird in MAIN_QUERY behandelt. In Bezug auf die letzte Abfrage sollte es also keine Rolle spielen, ob CTE2 tatsächlich irgendwelche Zeilen zurückgibt oder nicht.

Allerdings die Anwendung, die ich verwende (Business Objects) wirft einen Fehler, dass die Abfrage hat "keine Daten zu holen", wenn CTE2 keine Zeilen hat.

Ich möchte einen Weg um diese zu finden, um meine Abfrage zu ermöglichen, selbst wenn CTE2 null zurückgibt. Vielen Dank.

+3

Unrelated, aber: Sie sollten einen expliziten 'JOIN' Operator anstelle des alten veralteten implizite schließt sich in dem' where' Klausel beginnen. –

+1

Ich bin mir nicht sicher, ich verstehe ganz, was Sie wollen, aber ich denke, eine einfache Outer Join 'main_query' würde tun: 'von cte1 links Join cte2 auf cte1.pid = cte2.pid' –

+0

Danke - aber wie gesagt, die echte Abfrage ist sehr viel komplexer als das Beispiel, und ich bin nicht sicher, wie Joins funktionieren würde: Ich habe mindestens * drei * vorbereitende CTEs, die vor der Hauptabfrage ausgeführt werden müssen, und in jedem CTE gibt es eine Last von komplex Zeug geht weiter. Ich habe keine Ahnung, wie ich mit Joins fortfahren würde. Ich verstehe CTEs, also arbeite ich damit. Dankbar, wenn jemand helfen kann - danke. –

Antwort

1

CTE2 gibt das früheste Datum einer Anwendung zurück, die von derselben Personenkennung erstellt wurde, bei der die Anmeldung mehr als 18 Monate vor dem Anwendungsdatum von CTE1 liegt. Wie auch immer ... was ist, wenn es solche Anwendungen nicht gibt? CTE2 kann null Zeilen zurückgeben.

Sie können Ihre sub-query factoring (WITH ... AS (...)) Klauseln durch eine einfache analytische Funktion ersetzen:

Oracle-Setup:

CREATE TABLE PEOPLE (id) AS 
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 3; 

CREATE TABLE APPLICATIONS (id, person_id, "DATE") AS 
SELECT 1, 1, DATE '2015-01-01' FROM DUAL UNION ALL -- First row to return 
SELECT 2, 1, DATE '2014-01-01' FROM DUAL UNION ALL -- Within 18 months 
SELECT 3, 1, DATE '2013-01-01' FROM DUAL UNION ALL -- Before 18 months 
SELECT 4, 1, DATE '2012-01-01' FROM DUAL UNION ALL -- Before 18 months and min 
SELECT 5, 2, DATE '2015-01-02' FROM DUAL UNION ALL -- Second row to return 
SELECT 6, 2, DATE '2014-01-02' FROM DUAL UNION ALL -- Within 18 months 
SELECT 7, 3, DATE '2015-01-03' FROM DUAL UNION ALL -- Third row to return 
SELECT 8, 3, DATE '2013-07-03' FROM DUAL;   -- Exactly 18 months earlier 

Abfrage:

SELECT PID, 
     APPDATE, 
     CASE EARLIEST_APPDATE 
      WHEN APPDATE - INTERVAL '18' MONTH 
      THEN NULL 
      ELSE EARLIEST_APPDATE 
      END AS EARLIEST_APPDATE -- Included for the edge case where 
            -- EARLIEST_APPDATE is exactly 18 months 
            -- earlier as the RANGE BETWEEN is 
            -- inclusive. 
FROM (
    SELECT p.ID AS PID, 
     a."DATE" AS APPDATE, 
     MIN(a."DATE") OVER (PARTITION BY p.ID 
           ORDER BY  a."DATE" 
           RANGE BETWEEN UNBOUNDED PRECEDING 
              AND INTERVAL '18' MONTH PRECEDING) 
      AS EARLIEST_APPDATE 
    FROM PEOPLE p 
     INNER JOIN APPLICATIONS a 
     ON (a.PERSON_ID = p.ID) 
) 
WHERE APPDATE BETWEEN DATE '2015-01-01' AND DATE '2015-01-31' 

Ausgang:

 PID APPDATE    EARLIEST_APPDATE 
---------- ------------------- ------------------- 
     1 2015-01-01 00:00:00 2012-01-01 00:00:00 
     2 2015-01-02 00:00:00      
     3 2015-01-03 00:00:00      
+0

Ok, danke für deine Bemühungen - aber das hat nicht geholfen, denn wie ich in meiner Frage gesagt habe, ist die tatsächliche Abfrage sehr komplex, und das Beispiel, das ich gab, war nur eine Darstellung dessen, was ich tue.Ich habe nicht nur keine Ahnung von all dem "unbegrenzten vorhergehenden" Zeug, aber ich bin mir auch nicht sicher, ob es sogar möglich ist, Joins zu verwenden, um das zu ersetzen, was ich mit mehreren CTEs mache. Wie auch immer ... seit dem Posten habe ich einen Weg gefunden: geschachtelte Abfragen anstelle aller CTEs zu verwenden! Aber danke trotzdem :) –

+0

Ich schlage vor, Sie lesen die Dokumentation, da Sie eine Menge von Funktionen vermissen werden, die alles erheblich vereinfachen werden, wenn Sie nur Dinge entlassen, von denen Sie nichts wissen. Die "UNBOUNDED PRECEDING" ist die Windowing-Klausel von [analytic functions] (http://docs.oracle.com/cd/E11882_01/server.112/e41084/functions004.htm), die den Wert einschränken, den Sie für das Minimum finden um die gleiche ID zu haben und mehr als 18 Monate früher zu sein. – MT0

Verwandte Themen