Invalid identifier in double-nested query with ORDER BY and ROWNUM

夙愿已清 提交于 2019-12-11 20:43:28

问题


I'm on Oracle and I need to use both, ORDER BY and ROWNUM, in one request. I need to double-nest my inner query, because I want to apply ORDER BY first and select with ROWNUM=1 afterwards.

My data is filtered by O.ID on outmost level. However, I get an error in my inner query, because O.ID is an unknown identifier there.

What I want:

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!

The only way I get this working, is when I filter in the WHERE clause of the intermediate SELECT query. But this is slow, of course, since the inner SQL returns the entire data without filtering.

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;

How can I pass O.ID to the inner query or how can this query be redesigned, still keeping ORDER BY and ROWNUM work.


My final solution as suggested by Kim Berg Hansen and improved by rims:

(I had to use MIN() instead of MAX(), though)

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!

回答1:


In the scalar subquery you are using, you can only reference the tables from the "main" query "one nested level down", not any further down, as you have seen. (I believe this restriction is lifted in version 12, so maybe you can just upgrade your database? ;-)

In the scalar subquery you are trying to get the value of INSERTDATE column of the first row according to your ordering. That can also be written without nesting as follows:

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 tells the MAX function, that it should calculate the MAX only of those rows that rank first in the ORDER BY clause. So if your ORDER BY is "unique", MAX will be applied only to one row. If your ORDER BY is not "unique" and can have duplicates, you might think about whether you want the MAX or the MIN (or add something to the ORDER BY to make it unique.)

(If you had been on Oracle version 12, an alternative to the KEEP (DENSE_RANK trick would be to use the FIRST 1 ROW ONLY clause of the SELECT statement.)



来源:https://stackoverflow.com/questions/25616529/invalid-identifier-in-double-nested-query-with-order-by-and-rownum

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!