Oracle SQL重用子查询“WITH”子句辅助

时间:2016-03-01 09:51:56

标签: sql oracle subquery common-table-expression

我有一个SQL查询,我需要在其中获取子查询的输出并多次使用它。我现有的查询有效,但前提是每次需要时都重复子查询。不幸的是,子查询很复杂,并且需要时间来执行 - 这意味着多次迭代确实会使整个事情变慢。

我已经读过你可以使用“WITH”语句将子查询输出分配给变量,以便重用该变量。但是我遇到的问题是在子查询中,我需要引用主查询中的值。并且似乎如果我在主查询SELECT之前使用WITH - 则不能识别这些引用。我会给你一个简化的例子:

WITH
    DateX AS
    (
    SELECT
        MAX(TableSub.Date)
    FROM
        TableA TableSub
    WHERE
        TableSub.ID = TableMain.ID
        AND TableSub.Event = 'AnotherEvent'
        AND TableSub.Date BETWEEN '01-Jan-2015' AND '31-Dec-2015'
    )
SELECT
    TableMain.ID
FROM
    TableA TableMain
WHERE
    TableMain.Event = 'MainEvent'
    AND TableMain.Date >= DateX
    AND (
        SELECT
            TableSub2.ID
        FROM
            TableA TableSub2
        WHERE
            TableSub2.ID = TableMain.ID
            TableSub2.Event = 'ThirdEvent'
            AND TableSub2.Date <= DateX
        ) IS NULL

我希望这很清楚。它是我所拥有的简化版本,但您可以看到DateX在多个位置使用:在主查询中,在子查询中。但问题是当DateX由WITH定义时,我需要将ID链接回主查询的ID。它不起作用......

如果有任何建议,我将不胜感激。我做错了吗?有办法,还是只是不可能?如果是这样,那么我应该完全使用另一种方法吗?感谢。

2 个答案:

答案 0 :(得分:1)

更好的方法

SELECT ID
FROM   (
  SELECT ID,
         "Date",
         Event,
         LAST_VALUE( CASE Event WHEN 'AnotherEvent' THEN "Date" END IGNORE NULLS )
           OVER ( PARTITION BY ID ORDER BY "Date"
                  ROWS BETWEEN UNBOUNDED PRECEEDING AND UNBOUNDED FOLLOWING
                ) AS another_date,
         FIRST_VALUE( CASE Event WHEN 'ThirdEvent' THEN "Date" END IGNORE NULLS )
           OVER ( PARTITION BY ID ORDER BY "Date"
                  ROWS BETWEEN UNBOUNDED PRECEEDING AND UNBOUNDED FOLLOWING
                ) AS third_date
  FROM   TableA
  WHERE Event IN ( 'MainEvent', 'ThirdEvent' )
  OR    ( Event = 'AnotherEvent' AND EXTRACT( YEAR FROM "Date" ) = 2015 )
)
WHERE Event = 'MainEvent'
AND   "Date" >= another_date
AND   ( third_date IS NULL OR third_date > another_date );

答案 1 :(得分:0)

您需要在ID列上加入您的DateX CTE。类似的东西:

WITH
    DateX AS
    (
    SELECT
        TableSub.ID,
        MAX(TableSub.Date) AS MaxDate
    FROM
        TableA TableSub
    WHERE
        AND TableSub.Event = 'AnotherEvent'
        AND TableSub.Date >= DATE '2015-01-01'
        AND TableSub.Date < DATE '2016-01-01'
    GROUP BY
        TableSub.ID
    )
SELECT
    TableMain.ID
FROM
    TableA TableMain
JOIN
    DateX
ON
    DateX.ID = TableMain.ID
WHERE
    TableMain.Event = 'MainEvent'
    AND TableMain.Date >= DateX.MaxDate
    AND (
        SELECT
            TableSub2.ID
        FROM
            TableA TableSub2
        JOIN
            DateX
        ON
            DateX.ID = TableSub2.ID
        WHERE
            TableSub2.ID = TableMain.ID
            TableSub2.Event = 'ThirdEvent'
            AND TableSub2.Date <= DateX.MaxDate
        ) IS NULL

CTE还需要聚合的列别名;当你需要加入ID时,你需要包含它并按其分组。

最后一个子查询看起来很奇怪;如果您没有找到记录,则可能需要NOT EXISTS而不是IS NULL。也许你真正的查询是使用聚合,但即便这样也可能更快。

这仍然可能不是最好的方法,但从你的例子中很难说清楚。三次击中同一张桌子可能会不必要地花费很多。