我正在使用多个CTE在Oracle中构建一个非常复杂的SQL查询,后者的表达式依赖于前者的值。但是我发现如果其中一个先前的CTE不包含数据,则整个执行停止。例如:
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
当然,我意识到上面的例子本身就完全没有意义,但是我刚刚简化了这个例子来说明问题。 CTE2返回申请日期超过CTE1申请日期前18个月的同一人员身份证申请的最早日期。但是......如果没有这样的申请怎么办? CTE2能够返回零行。
您会注意到CTE2本身并未在最终查询中引用。在MAIN_QUERY中处理空CTE2。因此,关于最终查询,CTE2是否实际返回任何行都无关紧要。
然而,当CTE2没有行时,我正在使用的应用程序(Business Objects)会抛出一个错误,即查询“没有要获取的数据”。
我想找到一种解决方法,即使CTE2返回null也能使我的查询执行。感谢。
答案 0 :(得分:1)
CTE2返回申请日期超过CTE1申请日期前18个月的同一人员身份证申请的最早日期。但是......如果没有这样的申请怎么办? CTE2能够返回零行。
您可以使用简单的分析功能替换sub-query factoring(WITH ... AS ( ... )
)子句:
Oracle安装程序:
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
<强>查询强>:
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'
<强>输出强>:
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