Oracle 更新的多列和不同的连接条件

Posted

技术标签:

【中文标题】Oracle 更新的多列和不同的连接条件【英文标题】:Multiple Columns and different Join Conditions for Oracle update 【发布时间】:2020-07-28 23:46:41 【问题描述】:

我有 2 个表,1 个是位置,另一个是查找表。我必须查看查找表中的位置值,如果它们存在,则将它们标记为“Y”和“N”以及它们对应的值

我已经写了如下的个人更新声明:

**Location1,L1value**

Update Location
set (Location1,L1value) = 
(select UPPER(VAlue),'Y'  from Location_lookup  where  trim(Location1)=Location
where exists (select 1 from Location_lookup  where   trim(Location1)=Location);
commit;

**Location2,value**
Update Location
set (Location2,L2value) = 
(select UPPER(VAlue),'Y'  from Location_lookup  where  trim(Location2)=Location
where exists (select 1 from Location_lookup  where  trim(Location2)=Location);
commit;

同样适用于第三个标志和值。

有没有办法为所有三个条件编写单个更新?我寻找单个更新的原因是我有 10+ 百万条记录,我不想扫描记录三个不同的时间。查找表有超过 3200 万条记录。

【问题讨论】:

我的错。我已将第二个查询更新为指向右列 (Location2) 。查找有 32 多万条记录。 Location1,Location2,Location3 将在查找表中的位置列上匹配 【参考方案1】:

这是一个使用 Oracle 的批量 FORALL ... UPDATE 功能的解决方案。这不如纯 SQL 解决方案的性能好,但它更易于编码,效率差异对于现代企业服务器上的 1000 万行可能并不重要,尤其是如果这是一次性练习。

注意事项:

    你没有说 LOCATION 是否有主键。对于这个答案,我假设它有一个 ID 列。如果没有主键,该解决方案将不起作用,但如果您的表没有主键,您可能会遇到更大的问题。 您的问题提到将 FLAG 列设置为 “为 'Y' 和 'N'”,但所需的输出仅显示 'Y' 设置。我已经包含了'N' 的处理,但请看下面的结尾。
declare
  cursor get_locations is
    with lkup as (
      select *
      from   location_lookup
    )
    select  locn.id
           ,locn.location1
           ,upper(lup1.value)          as l1value
           ,nvl2(lup1.value, 'Y', 'N') as l1flag  
           ,locn.location2
           ,upper(lup2.value)          as l2value
           ,nvl2(lup2.value, 'Y', 'N') as l2flag  
           ,locn.location3
           ,upper(lup3.value)          as l3value
           ,nvl2(lup3.value, 'Y', 'N') as l3flag
    from  location locn
          left outer join lkup lup1 on trim(locn.location1) = lup1.location 
          left outer join lkup lup2 on trim(locn.location2) = lup2.location 
          left outer join lkup lup3 on trim(locn.location3) = lup3.location 
    where lup1.location is not null
    or    lup2.location is not null 
    or    lup3.location is not null;
    
  type t_locations_type is table of get_locations%rowtype index by binary_integer;
  t_locations t_locations_type;
  
begin

  open get_locations;
  
  loop
    fetch get_locations bulk collect into t_locations limit 10000;
    exit when t_locations.count() = 0;
    
    forall idx in t_locations.first() .. t_locations.last()
      update location
      set    l1value = t_locations(idx).l1value 
            ,l1flag  = t_locations(idx).l1flag
            ,l2value = t_locations(idx).l2value 
            ,l2flag  = t_locations(idx).l2flag
            ,l3value = t_locations(idx).l3value 
            ,l3flag  = t_locations(idx).l3flag
      where id = t_locations(idx).id;
                  
  end loop;
  
  close get_locations; 
  
end;
/

有一个工作演示on db<>fiddle here。演示输出与查询中发布的示例输出不完全匹配,因为这与给定的输入数据不匹配。


将标志设置为“Y”还是“N”?

上面的代码在查找表上使用了左外连接。如果找到一行,NVL2() 函数将返回“Y”,否则返回“N”。这意味着始终填充标志列,无论值列是否存在。例外情况是在任何位置的 LOCATION_LOOKUP 中没有匹配的行(在我的演示中为ID=4000)。在这种情况下,标志列将为空。这种不一致源于问题中的不一致。

解决它:

如果您希望使用 'N' 填充所有标志列,请从 get_locations 游标查询中删除 WHERE 子句。 如果您不想将标志设置为'N',请相应更改 NVL2() 函数调用:nvl2(lup1.value, 'Y', null) as l1flag

【讨论】:

非常感谢。它适用于示例数据。与基于集合的查询相比,这些大容量的性能如何......游标基本上很慢。但我从未尝试过批量更新游标。再次感谢。 这里展示的游标是为批量操作而设计的。这些是面向集合的语句,而不是逐行的。

以上是关于Oracle 更新的多列和不同的连接条件的主要内容,如果未能解决你的问题,请参考以下文章

oracle 连接

连接多列且一列为空时的空格 - Oracle

如何将oracle 中一条数据拆分成多条

oracle连接总结(内连接外连接自然连接,交叉连接,自连接)

条件连接性能优化 - Oracle SQL

数据框在多列上连接,pyspark中的列有一些条件[重复]