Sql 具有ORDER BY和ROWNUM的双嵌套查询中的标识符无效

Sql 具有ORDER BY和ROWNUM的双嵌套查询中的标识符无效,sql,oracle,nested,sql-order-by,rownum,Sql,Oracle,Nested,Sql Order By,Rownum,我在Oracle上,我需要在一个请求中同时使用,orderby和ROWNUM。我需要双倍嵌套我的内部查询,因为我想先应用ORDER BY,然后选择withROWNUM=1 我的数据由最外层的O.ID过滤。但是,我在内部查询中得到一个错误,因为O.ID是一个未知标识符 我想要的是: SELECT O.INSERTDATE OrderCreateDate, -- Determine delivery date (SELECT INSERTDATE FROM ( SELECT

我在Oracle上,我需要在一个请求中同时使用,
orderby
ROWNUM
。我需要双倍嵌套我的内部查询,因为我想先应用
ORDER BY
,然后选择with
ROWNUM=1

我的数据由最外层的
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 BY
ROWNUM
工作


我的最终解决方案由Kim Berg Hansen建议,rims改进:

(我不得不使用
MIN()
而不是
MAX()


在您正在使用的标量子查询中,您只能从“一个嵌套级别”的“主”查询中引用表,如您所见,不能再往下引用。(我相信这个限制在第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函数,它应该只计算ORDER BY子句中排名第一的行的MAX。因此,如果您的ORDER BY是“唯一的”,MAX将只应用于一行。如果您的ORDER BY不是“唯一的”,并且可以有重复的,您可能会考虑是要MAX还是MIN(或通过向订单中添加某些内容使其具有唯一性。)


(如果您使用的是Oracle版本12,那么KEEP的另一种替代方法(densite_RANK技巧是使用SELECT语句的前1行ONLY子句。)

这些都是大型查询。相关部分并不突出。请查看您是否可以筛选出不相关的内容,并发布一个简单的示例,再现与问题无关的问题,但您可以使用从日期-时间中删除时间部分来简化/改进查询。更改:
到\u日期(到\u字符(DD.INSERTDATE,'ddmmyyyyy'),'ddmmyyyyy'))
to
TRUNC(DD.INSERTDATE)
@rims:感谢您指出这一点。谢谢。我今天仔细研究了分析函数,我可以将您建议的模式应用到另一个用例中。Oracle在这里提供了非常有用的功能!
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!