【问题标题】:Oracle SQL re-use subquery "WITH" clause assistanceOracle SQL 重用子查询“WITH”子句辅助
【发布时间】:2016-03-01 09:51:55
【问题描述】:

我有一个 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。而且它不工作......

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

【问题讨论】:

  • 请不要使用字符串文字来匹配日期 - 即'01-Jan-2015'。它会在 NLS_DATE_FORMAT matches 时工作(因为 Oracle 将使用该参数作为格式掩码执行隐式 TO_DATE),但如果该参数发生更改,查询将失败(并且由于代码不会调试,因此会很痛苦) t 已经改变,但它会开始抛出异常)。最好使用 ANSI 日期文字 - 即 DATE '2015-01-01'
  • 好的,感谢您提供有关日期格式的提示。知道如何回答这个问题吗? :)

标签: sql oracle subquery common-table-expression


【解决方案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 );

【讨论】:

    【解决方案2】:

    您需要在 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,因此您需要包含该 ID 并按其分组。

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

    这可能仍然不是最好的方法,但很难从您的示例中看出。在同一张桌子上打三遍可能会不必要地昂贵。

    【讨论】:

      猜你喜欢
      • 2015-07-30
      • 1970-01-01
      • 1970-01-01
      • 2011-08-18
      • 2016-04-25
      • 1970-01-01
      • 1970-01-01
      • 2021-12-14
      • 1970-01-01
      相关资源
      最近更新 更多