2014-09-02 121 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