多列匹配下的Oracle自连接 - CONNECT BY?

3

我有一个来自----的查询需求。 尝试使用CONNECT BY解决,但似乎无法得到所需的结果。


表(简化):

create table CSS.USER_DESC (
    USER_ID          VARCHAR2(30) not null,
    NEW_USER_ID      VARCHAR2(30),
    GLOBAL_HR_ID     CHAR(8)
)

-- USER_ID       is the primary key
-- NEW_USER_ID   is a self-referencing key
-- GLOBAL_HR_ID  is an ID field from another system

有两个用户数据来源(数据源)......在更新信息时,我必须注意它们中的任何一个是否存在错误。


场景:

  1. 给用户分配新的用户ID......旧记录相应地被设置并停用(通常是承包商转为全职员工的重命名)
  2. 用户离开一段时间后回来。 人力资源部未能向我们发送旧的用户ID,以便我们可以连接帐户。
  3. 系统出了问题,没有在旧记录上设置新的用户ID。
  4. 数据可能以其他一百种方式出现问题。


我需要知道以下内容是同一用户,并且不能依赖名称或其他字段......它们在匹配记录之间不同:

ROOTUSER    NUMROOTS   NODELEVEL  ISLEAF    USER_ID    NEW_USER_ID  GLOBAL_HR_ID   USERTYPE      LAST_NAME         FIRST_NAME  
-----------------------------------------------------------------------------------------------------------------------------
EX0T1100    2          1          0         EX0T1100   EX000005                    CONTRACTOR    VON DER HAAVEN    VERONICA    
EX0T1100    2          2          1         EX000005                00126121       EMPLOYEE      HAAVEN, VON DER   VERONICA    
GL110456    1          1          1         GL110456                00126121       EMPLOYEE      VONDERHAAVEN      VERONICA    
EXOT1100EX000005已通过NEW_USER_ID字段正确连接。 由于重命名发生在没有全局HR ID的情况下,因此EX0T1100没有HR ID。 EX000005被分配了一个新的用户ID“GL110456”,这两个用户仅通过具有相同的全局HR ID进行连接。
清理数据不是一种选择。
目前查询的内容:
select connect_by_root cud.user_id RootUser, 
       count(connect_by_root cud.user_id) over (partition by connect_by_root cud.user_id) NumRoots, 
       level NodeLevel, connect_by_isleaf IsLeaf, --connect_by_iscycle IsCycle,
       cud.user_id, cud.new_user_id, cud.global_hr_id,
       cud.user_type_code UserType, ccud.last_name, cud.first_name
from   css.user_desc cud
where  cud.user_id in ('EX000005','EX0T1100','GL110456')
-- Using this so I don't get sub-users in my list of root users...
-- It complicates the matches with GLOBAL_HR_ID, however
start with cud.user_id not in (select cudsub.new_user_id 
                               from   css.user_desc cudsub 
                               where  cudsub.new_user_id is not null)
connect by nocycle (prior new_user_id = user_id);


我尝试了各种CONNECT BY子句,但都不太合适:

-- As a multiple CONNECT BY
connect by nocycle (prior global_hr_id = global_hr_id)
connect by nocycle (prior new_user_id  = user_id)

-- As a compound CONNECT BY
connect by nocycle ((prior new_user_id = user_id)
                     or (prior global_hr_id = global_hr_id 
                         and user_id != prior user_Id))

联接两个CONNECT BY查询不起作用...我无法获得层级关系。
以下是我想要看到的内容...我可以使用一个需要去重并用作子查询的结果集。我也可以使用ROOTUSER列中的任何三个用户ID...我只需要知道它们是相同的用户。
ROOTUSER    NUMROOTS   NODELEVEL  ISLEAF    USER_ID    NEW_USER_ID  GLOBAL_HR_ID   USERTYPE      LAST_NAME         FIRST_NAME  
-----------------------------------------------------------------------------------------------------------------------------
EX0T1100    3          1          0         EX0T1100   EX000005                    CONTRACTOR    VON DER HAAVEN    VERONICA    
EX0T1100    3          2          1         EX000005                00126121       EMPLOYEE      HAAVEN, VON DER   VERONICA    
EX0T1100    3         (2 or 3)    1         GL110456                00126121       EMPLOYEE      VONDERHAAVEN      VERONICA    


有什么想法吗?


更新

Nicholas,你的代码看起来非常正确……目前,在global_hr_id为空时,lead(user_id)over(partition by global_hr_id)会得到错误的结果。例如:

USER_ID   NEW_USER_ID   CHAINNEWUSER   GLOBAL_HR_ID   LAST_NAME   FIRST_NAME
FP004468                FP004469                      AARON       TIMOTHY
FP004469                                              FOONG       KOK WAH

我经常希望在分区中将null视为单独的记录,但我从未找到过使ignore nulls起作用的方法。以下代码实现了我想要的效果:

decode(global_hr_id,null,null,lead(cud.user_id ignore nulls) over (partition by global_hr_id order by user_id)

...但一定有更好的方法。我还没有能够在完整的用户数据(约40,000个用户)上完成查询。 global_hr_idnew_user_id都已被索引。


更新

查询大约在750秒后返回...时间长,但可管理。它返回93k条记录,因为我没有很好的方法来过滤掉根目录中的二级命中 - 你可以使用start with global_hr_id is null,但不幸的是,并非总是这种情况。我需要再想一下如何过滤这些内容。

我已经尝试添加更复杂的开始子句,但我发现单独运行时小于1秒...但是一起运行需要90分钟 >.<

再次感谢您的帮助...继续努力。

1个回答

3

你只提供了一个用户的样本数据,最好再多一些。无论如何,让我们来看看这个。

SQL> with user_desc(USER_ID, NEW_USER_ID,  GLOBAL_HR_ID)as(
  2    select 'EX0T1100',  'EX000005',  null          from dual union all
  3    select 'EX000005',   null,       00126121      from dual union all
  4    select 'GL110456',   null,       00126121      from dual
  5  )
  6  select connect_by_root(user_id) rootuser
  7       , count(connect_by_root(user_id)) over(partition by connect_by_root(user_id)) numroot
  8       , level nodlevel
  9       , connect_by_isleaf
 10       , user_id
 11       , new_user_id
 12       , global_hr_id
 13    from (select user_id
 14               , coalesce(new_user_id, usr) new_user_id1
 15               , new_user_id
 16               , global_hr_id
 17            from ( select user_id
 18                        , new_user_id
 19                        , global_hr_id
 20                        , decode(global_hr_id,null,null,lead(user_id) over (partition by global_hr_id order by user_id)) usr
 21                    from user_desc
 22                 )
 23         )
 24  start with global_hr_id is null
 25  connect by prior new_user_id1 = user_id
 26  ;

结果:

ROOTUSER    NUMROOT   NODLEVEL CONNECT_BY_ISLEAF USER_ID  NEW_USER_ID GLOBAL_HR_ID
-------- ---------- ---------- ----------------- -------- ----------- ------------
EX0T1100          3          1                 0 EX0T1100 EX000005    
EX0T1100          3          2                 0 EX000005                   126121
EX0T1100          3          3                 1 GL110456                   126121

哇,我真的不配。我现在正在尝试这个...我试图执行时临时表空间不足,但我认为我可以调整一下让它工作。我会告诉你的,谢谢! - James King
PS:很难想出一个完全代表性的数据集……如果我能让这个案例起作用,我认为大多数其他人也会跟进。将进行测试以确保。 - James King
发布了一个带有更多信息的更新...继续使用你给我的东西进行尝试。 - James King
我不知道是我先完成还是你先完成了:) 但我会认为你的回复意味着确实没有更好的方法。 - James King
我在采纳答案后添加了悬赏,因为这个答案非常有帮助和启发性。学到了很多新的Oracle技巧:)谢谢! - James King

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接