【问题标题】:SQL join on date but use lead to pull priorSQL 加入日期,但使用前导拉
【发布时间】:2015-10-13 16:50:15
【问题描述】:

我无法让它工作。我正在查询 S 表以获取项目列表,并希望加入 P 表以提取与 S + 前一个日期相同的日期的数据,只要可能使用了 LEAD 函数。

问题是 P 表很大,除非给出特定日期或小范围,否则查询需要很长时间。由于我是按日期加入标准普尔的,因此 LEAD 似乎无法提取前一个日期的数据。

还有其他选择吗?前面的 P.TDATE 通常只是之前的营业日期,但有时可能是一年多以前。

SELECT S.ID,
       S.EDATE,
       S.PDATE,
       S.FACTOR,
       S.PTYPE,
       P.TDATE,
       P.PRICE,
       P.PTYPE,
       LEAD(P.TDATE) OVER (PARTITION BY P.ID
                           ORDER BY P.TDATE DESC) AS PRIOR_DATE,
       LEAD(P.PRICE) OVER (PARTITION BY P.ID
                           ORDER BY P.TDATE DESC) AS PRIOR_PRICE,
       LEAD(P.PTYPE) OVER (PARTITION BY P.ID
                           ORDER BY P.TDATE DESC) AS PRIOR_PTYPE
FROM S
LEFT JOIN P ON P.ID = S.ID
AND P.TDATE = S.EDATE
WHERE S.PERIOD = 'D'
  AND S.PTYPE IN ('A',
                  'G',
                  'Q',
                  'Y')
ORDER BY S.EDATE ;

我得到了所有正确的数据,除了prior_date、prior_price 和prior_ptype 在它们不应该的时候都是空白的。

日期字段中没有 NULL 值。

是否有另一种基于 S.EDATE 查询 P 表的有效方法,而不是在左连接中使用 P.TDATE = S.EDATE?我相信这种情况是由于 LEAD 函数没有其他数据可查看而导致“先前”列上没有显示任何内容的原因。

谢谢,

【问题讨论】:

  • 这是错字吗:ORDER BY S.EDATE, 那个逗号?
  • 是的。我删除了它。
  • 因为您是按日期按 desc 顺序排序的,所以 NULL 值将是第一个。如果你在函数中包含 NULL LAST 规则,我认为你会得到你想要的结果.. LEAD(P.TDATE) OVER (PARTITION BY P.ID ORDER BY P.TDATE DESC NULLS LAST) AS PRIOR_DATE,...
  • 顺便说一句,LEAD... ORDER BY ... DESCLAG... ORDER BY ... ASC 相同
  • 任何表中都没有空日期值。因此,按 P.TDATE DESC 排序应该给出前一个日期行。

标签: sql oracle join lead


【解决方案1】:

你能调整下面的查询来重现这个案例吗

WITH a
     AS (SELECT 1 id, TO_DATE ('01-01-2011', 'mm-dd-yyyy') dat, 0 val
           FROM DUAL
         UNION ALL
         SELECT 1 id, TO_DATE ('01-02-2011', 'mm-dd-yyyy') dat, 1 val
           FROM DUAL
         UNION ALL
         SELECT 2 id, TO_DATE ('01-03-2011', 'mm-dd-yyyy') dat, 2 val
           FROM DUAL)
SELECT id, dat, val, LEAD (val) OVER (PARTITION BY id ORDER BY dat DESC) pre_val
  FROM a

【讨论】:

    【解决方案2】:

    http://docs.oracle.com/cd/E11882_01/server.112/e41084/functions004.htm#SQLRF06174

    升序 | DESC 指定排序顺序(升序或降序)。 ASC 是默认设置。

    NULLS FIRST | NULLS LAST 指定返回的包含空值的行应该出现在排序顺序的第一个还是最后一个。

    NULLS LAST 是默认的升序,NULLS FIRST 是默认的降序。

    分析函数始终按照函数的 order_by_clause 中指定的顺序对行进行操作。但是,函数的 order_by_clause 并不能保证结果的顺序。使用查询的 order_by_clause 来保证最终结果的排序。

        SELECT S.ID,
               S.EDATE,
               S.PDATE,
               S.FACTOR,
               S.PTYPE,
               P.TDATE,
               P.PRICE,
               P.PTYPE,
               LEAD(P.TDATE) OVER (PARTITION BY P.ID
                                   ORDER BY P.TDATE DESC NULLS LAST) AS PRIOR_DATE,
               LEAD(P.PRICE) OVER (PARTITION BY P.ID
                                   ORDER BY P.TDATE DESC NULLS LAST) AS PRIOR_PRICE,
               LEAD(P.PTYPE) OVER (PARTITION BY P.ID
                                   ORDER BY P.TDATE DESC NULLS LAST) AS PRIOR_PTYPE
        FROM S
        LEFT JOIN P ON P.ID = S.ID
        AND P.TDATE = S.EDATE
        WHERE S.PERIOD = 'D'
          AND S.PTYPE IN ('A',
                          'G',
                          'Q',
                          'Y')
        ORDER BY S.EDATE ;
    

    【讨论】:

    • 任何表中都没有空日期值。我认为我没有得到 LEAD (Prior) 列的任何结果是由于在确切日期加入,因此前导函数没有要查看的先前日期。我希望找到一种有效的方法来查找前一行,因为 P 表需要很长时间才能查询大日期范围。
    【解决方案3】:

    如果您由于 LEFT JOIN 而得到 NULL,您可以尝试将 IGNORE NULLS 选项添加到 LEAD

           LEAD(P.TDATE IGNORE NULLS) OVER (PARTITION BY P.ID
                               ORDER BY P.TDATE DESC NULLS LAST) AS PRIOR_DATE,
    

    相同
           LAG(P.TDATE IGNORE NULLS) OVER (PARTITION BY P.ID
                               ORDER BY P.TDATE) AS PRIOR_DATE,
    

    或将计算移至派生表/内联视图:

    SELECT S.ID,
           S.EDATE,
           S.PDATE,
           S.FACTOR,
           S.PTYPE,
           P.TDATE,
           P.PRICE,
           P.PTYPE,
           P.PRIOR_DATE,
           P.PRIOR_PRICE,
           P.PRIOR_PTYPE
    FROM S
    LEFT JOIN
     ( SELECT ...,
           LEAD(P.TDATE) OVER (PARTITION BY P.ID
                               ORDER BY P.TDATE DESC NULLS LAST) AS PRIOR_DATE,
           LEAD(P.PRICE) OVER (PARTITION BY P.ID
                               ORDER BY P.TDATE DESC NULLS LAST) AS PRIOR_PRICE,
           LEAD(P.PTYPE) OVER (PARTITION BY P.ID
                               ORDER BY P.TDATE DESC NULLS LAST) AS PRIOR_PTYPE
    
       FROM P 
     ) P ON P.ID = S.ID
    AND P.TDATE = S.EDATE
    WHERE S.PERIOD = 'D'
      AND S.PTYPE IN ('A',
                      'G',
                      'Q',
                      'Y')
    ORDER BY S.EDATE ;
    

    【讨论】:

    • 我相信这最终会奏效,但运行需要 30 多分钟。 P 表需要很长时间才能提取所有数据,因为它会提取每个 ID 的所有内容,然后将其限制为 S.EDATE
    【解决方案4】:

    以叙述的形式思考这个问题:你在 S 中有一行。你想在 P 中找到两行:

    1. 直接匹配 S ID & EDATE 的 P 行。
    2. 您找到的第一个 P 行之前的行。

    您实际上必须找到基于第一个 P 行的第二个 P 行 - 它不能神奇地“在那里”让您以正确的 LEAD 化身向后走()。

    这样看,您试图找到最大 TDATE 小于 #1P 行的 TDATE 的 #2P 行。 dnoeth 的回答是解决这个问题的一种方法——但正如你所见,它非常慢(他实际上正在构建与 S 中的任何 ID 匹配的所有 P 行的完整历史记录——如果不是针对 P 中的每个 ID——然后使用该构建的历史记录来“采摘" 找到#1P 行后的#2P 行)。

    这应该是一种更好的方法,尤其是如果 P 在 ID 和 TDATE 上都被索引:

    SELECT T.*  , P3.PRICE PRIOR_PRICE, P3.PTYPE PRIOR_PTYPE
    FROM 
        (
        SELECT S.ID,
               S.EDATE,
               S.PDATE,
               S.FACTOR,
               S.PTYPE S_PTYPE,
               P.TDATE,
               P.PRICE,
               P.PTYPE P_PTYPE,
               (SELECT MAX(P2.TDATE)  FROM P P2 WHERE P2.ID = P.ID AND P2.TDATE < P.TDATE) PRIOR_TDATE
        FROM S
        LEFT JOIN P ON P.ID = S.ID
        AND P.TDATE = S.EDATE
        WHERE S.PERIOD = 'D'
          AND S.PTYPE IN ('A','G','Q','Y')
        ) T
    LEFT JOIN P P3 ON P3.ID = T.ID AND P3.TDATE = T.PRIOR_TDATE
    ORDER BY T.EDATE ;
    

    【讨论】:

    • 感谢您的回复。这似乎是我一直在寻找的。我明天试一试。
    猜你喜欢
    • 2018-08-02
    • 2012-11-08
    • 2011-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-01
    • 1970-01-01
    相关资源
    最近更新 更多