【问题标题】:Connect By query with recurring hierarchical checks使用重复的分层检查通过查询连接
【发布时间】:2013-09-03 12:55:49
【问题描述】:

我已经编写了以下查询,但不幸的是它不起作用。我想要实现的是,我为每一行选择的日期是客户关系的生效日期。

ci_per_per 表包含以下列:

per_id1per_id2start_dtend_dt

per_id1 是父级,per_id2 是子级。

start_dtend_dt 定义关系的持续时间

SELECT
  CONNECT_BY_ROOT per_id2                                                           AS per_id2,
  per_id1,
  (CASE WHEN start_dt < (PRIOR start_dt1)
            THEN (PRIOR start_dt1) 
            ELSE start_dt 
        END) AS start_dt1,
  (CASE WHEN end_dt > (PRIOR end_dt1) 
             THEN (PRIOR end_dt1) 
             ELSE end_dt 
        END)         AS end_dt1,
  level                                                                             AS "PER_LEVEL"
  FROM ci_per_per
  CONNECT BY NOCYCLE PRIOR per_id1 = per_id2

我得到的 sql 错误是:END_DT1: Invalid identifier.

有没有办法在 oracle 中实现这个功能?

示例场景:

per_id1 | per_id2 |   start_dt  |   end_dt
-------------------------------------------
B       |   A     |  01-01-2011 | 01-01-2011
C       |   B     |  01-01-2010 | 01-01-2010
E       |   B     |  01-01-2011 | 01-01-2014
D       |   C     |  01-01-2009 | 01-01-2015

现在我希望所有记录都以01-01-2011 作为开始日期。但是,如果层次结构颠倒(意味着交换 per_id_1 和 Per_id2 值),则记录应按原样显示。

请注意,对于上面的示例,我们需要使用带有per_id2='A' 条件的start with 子句来运行它。此外,请了解如果您使用 per_id2='B' 运行查询,结果可能会发生变化

因此,当我以per_id2='B' 开头时,结果应该使所有记录都为01-01-2010 而不是01-01-2011

现在有了结束日期,所有记录都应该是01-01-2011,除了带有 E 的记录和带有 A 的记录。其中结束日期将是 null(假设我们从 A 开始)。

【问题讨论】:

  • 我怀疑你必须使用递归子查询分解(递归 CTE)才能实现这一点。
  • 您能详细说明一下吗?提前致谢!
  • 您是否试图获取层次结构中最低的开始日期和结束日期?
  • 仍然不完全清楚你想要什么。如果在您的示例中,第二行 (C/B) 是 01-01/2013 怎么办?您想要层次结构中任意位置的最高值,还是当前级别以下的最高值?
  • @Joe,谢谢提问,我想要低于当前水平的最高值。

标签: sql database oracle hierarchy connect-by


【解决方案1】:

Oracle 层次结构和 CTE 的组合将产生您想要的结果:

WITH cte AS 
(SELECT 
  per_id1,
  max(connect_by_root start_dt) as max_start,
  min(connect_by_root end_dt) as min_end
FROM 
  ci_per_per
CONNECT BY 
  NOCYCLE PRIOR per_id1 = per_id2
GROUP BY 
  per_id1)
SELECT
  ci_per_per.per_id1,
  ci_per_per.per_id2,
  cte.max_start,
  cte.min_end
FROM
  cte
  join ci_per_per on cte.per_id1=ci_per_per.per_id1

SQLFiddle here你的例子相当简单,所以我不能确定这个逻辑是否适用于你所有的测试用例。

EDIT更新为最低end_dt

【讨论】:

  • 做得很好,但是如果您从我的查询中看到选择列,那么您就会明白其中有根连接。此外,在不同的日期范围内,一位父母可以有多个孩子(但是,在任何特定时间只有一个关系处于活动状态)
  • 您能否用其他场景更新问题以及预期结果应该是什么?
  • 更新了end_dt的答案。
  • 使用start with per_id1='A' 没有意义,因为 A 位于层次结构的顶部——不会生成任何行。见这里:sqlfiddle.com/#!4/56f7d/10/0
  • 实际上 A 位于层次结构的底部。我已经提到了per_id2='A'。也许这个 Fiddle 可以帮助你:sqlfiddle.com/#!4/56f7d/15
【解决方案2】:

我一直在尝试解决这个问题并找到了解决方案。

如果您认为以下 SQL 有任何问题,请纠正我:

select *
  from (SELECT root as per_id2,
               per_id1,
               PER_LEVEL,
               (case
                 when max(connect_by_root start_dt) >= max(start_dt) then
                  max(connect_by_root start_dt)
                 else
                  max(start_dt)
               end) as max_start,
               (case
                 when min(connect_by_root end_dt) <= min(end_dt) then
                  min(connect_by_root end_dt)
                 else
                  min(end_dt)
               end) AS min_end
          FROM (SELECT connect_by_root per_id2 AS root,
                       per_id1,
                       per_id2,
                       level as per_level,
                       (case
                         when connect_by_root start_dt >= start_dt then
                          connect_by_root start_dt
                         else
                          start_dt
                       end) as start_dt,
                       (case
                         when connect_by_root end_dt <= end_dt then
                          connect_by_root end_dt
                         else
                          end_dt
                       end) as end_dt
                  FROM ci_per_per
                CONNECT BY NOCYCLE PRIOR per_id1 = per_id2)
        CONNECT BY NOCYCLE PRIOR per_id1 = per_id2
               and PRIOR root = root
         GROUP BY per_id1, per_id2, root, per_level)
 where max_start <= min_end 

这是相同的fiddle

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-06
    • 2020-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 2016-11-01
    相关资源
    最近更新 更多