2014-09-02 82 views
0

我在Oracle上,我需要在一個請求中同時使用ORDER BYROWNUM。我需要雙重嵌入我的內部查詢,因爲我想申請ORDER BY第一個並選擇ROWNUM=1然後使用ORDER BY和ROWNUM的雙重嵌套查詢中的標識符無效

我的數據最多可以經由O.ID進行過濾。但是,我在我的內部查詢中遇到錯誤,因爲O.ID在那裏是未知標識

我想要什麼:

SELECT 
    O.INSERTDATE OrderCreateDate, 

    -- Determine delivery date 
    (SELECT INSERTDATE FROM (
    SELECT OP2.FK_ORDER, DD.ID, DD.INSERTDATE FROM MY_DELIVERYDATE_TABLE DD 
     JOIN MY_ORDERPOS_TABLE OP2 ON DD.FK_ORDERPOS=OP2.ID 
     LEFT OUTER JOIN MY_ORDER_TABLE O2 ON OP2.FK_ORDER=O2.ID 
     WHERE OP2.FK_ORDER=O.ID AND -- This gives me "Invalid identifier O.ID" 
      DD.DELFLAG IS NULL AND OP2.DELFLAG IS NULL 
     ORDER BY DD.CLOSED ASC, ABS(TRUNC(CURRENT_DATE-TO_DATE(TO_CHAR(DD.INSERTDATE, 'DDMMYYYY'), 'DDMMYYYY'))) ASC 
) WHERE ROWNUM=1) DeliveryDate 

FROM MY_ORDER_TABLE O 
WHERE O.ID = 620; -- ID goes here! 

我得到這個工作的唯一辦法,就是當我在中間SELECT查詢WHERE子句中進行篩選。但是這當然很慢,因爲內部SQL返回整個數據而沒有過濾。

SELECT 
    O.INSERTDATE OrderCreateDate, 

    -- Determine delivery date 
    (SELECT INSERTDATE FROM (
    SELECT OP2.FK_ORDER, DD.ID, DD.INSERTDATE FROM MY_DELIVERYDATE_TABLE DD 
     JOIN MY_ORDERPOS_TABLE OP2 ON DD.FK_ORDERPOS=OP2.ID 
     LEFT OUTER JOIN MY_ORDER_TABLE O2 ON OP2.FK_ORDER=O2.ID 
     WHERE DD.DELFLAG IS NULL AND OP2.DELFLAG IS NULL 
     ORDER BY DD.CLOSED ASC, ABS(TRUNC(CURRENT_DATE-TO_DATE(TO_CHAR(DD.INSERTDATE, 'DDMMYYYY'), 'DDMMYYYY'))) ASC 
) WHERE ROWNUM=1 AND FK_ORDER=O.ID) DeliveryDate -- Filtering here 

FROM MY_ORDER_TABLE O 
WHERE O.ID = 620; 

如何傳遞O.ID到內部查詢或這怎麼能查詢被重新設計,仍保持ORDER BYROWNUM工作。


我的最終解決方案由金伯格漢森的建議,並通過輪輞改進:

(我不得不使用MIN()代替MAX(),雖然)

SELECT 
    O.INSERTDATE OrderCreateDate, 

    -- Determine delivery date 
    (SELECT MIN(DD.INSERTDATE) KEEP (DENSE_RANK FIRST ORDER BY 
    DD.CLOSED ASC, ABS(TRUNC(CURRENT_DATE-TRUNC(DD.INSERTDATE))) ASC) 
    FROM MY_DELIVERYDATE_TABLE DD 
    JOIN MY_ORDERPOS_TABLE OP2 ON DD.FK_ORDERPOS=OP2.ID 
    LEFT OUTER JOIN MY_ORDER_TABLE O2 ON OP2.FK_ORDER=O2.ID 
    WHERE OP2.FK_ORDER=O.ID AND 
     DD.DELFLAG IS NULL AND OP2.DELFLAG IS NULL 
) DeliveryDate 

FROM MY_ORDER_TABLE O 
WHERE O.ID = 620; -- ID goes here! 
+0

這些是大型查詢。相關部分並不完全突出。查看是否可以過濾出無關緊要的內容併發布可重現此問題的簡單示例 – Andomar 2014-09-02 06:14:25

+2

與問題無關,但可以通過使用[TRUNC(date)](http://docs.oracle.com/)來簡化/改進查詢cd/B28359_01/server.111/b28286/functions209.htm)從日期時間中刪除時間部分。更改:'TO_DATE(TO_CHAR(DD.INSERTDATE,'DDMMYYYY','DDMMYYYY')''TRUNC(DD.INSERTDATE)' – Rimas 2014-09-02 07:34:35

+0

@rims:謝謝你指出。 – user1438038 2014-09-02 20:45:11

回答

2

在標量子查詢您正在使用,您只能參考「主要」查詢「一個嵌套級別下」的表格,而不是更進一步,如您所見。 (我相信這個限制在版本12中解除了,所以也許你可以升級你的數據庫?;-)

在標量子查詢中,你試圖根據你的排序得到第一行的INSERTDATE列的值。這也可以在不嵌套寫入如下:

SELECT 
O.INSERTDATE OrderCreateDate, 

-- Determine delivery date 
(SELECT MAX(DD.INSERTDATE) KEEP (
      DENSE_RANK FIRST ORDER BY 
      DD.CLOSED ASC, ABS(TRUNC(CURRENT_DATE-TO_DATE(TO_CHAR(DD.INSERTDATE, 'DDMMYYYY'), 'DDMMYYYY'))) ASC 
     ) 
    FROM MY_DELIVERYDATE_TABLE DD 
    JOIN MY_ORDERPOS_TABLE OP2 ON DD.FK_ORDERPOS=OP2.ID 
    LEFT OUTER JOIN MY_ORDER_TABLE O2 ON OP2.FK_ORDER=O2.ID 
    WHERE OP2.FK_ORDER=O.ID AND -- This will no longer give "Invalid identifier O.ID" 
     DD.DELFLAG IS NULL AND OP2.DELFLAG IS NULL 
) DeliveryDate 

FROM MY_ORDER_TABLE O 
WHERE O.ID = 620; -- ID goes here! 

KEEP(DENSE_RANK FIRST告訴MAX功能,它應該計算MAX 只有那些在ORDER BY子句中排名第一左右。如果你的ORDER BY是「唯一的」,MAX將僅適用於一個行。如果你的ORDER BY不是「唯一的」並且可能有重複,你可能會考慮是否需要MAX或MIN到ORDER BY以使其唯一)。

(如果您使用的是Oracle版本12,則可選擇第e KEEP(DENSE_RANK技巧是使用SELECT語句的FIRST 1 ROW ONLY子句。)

+0

謝謝。我今天仔細研究了分析函數,我也可以將你建議的模式應用到另一個用例中。 Oracle在此提供的非常有用的功能! – user1438038 2014-09-02 20:57:20