【问题标题】:NULL values not found in cursor游标中未找到 NULL 值
【发布时间】:2015-09-11 05:52:09
【问题描述】:

我正在尝试:

创建一个光标,获取商店中所有商品的当前价格。 我使用 MERGE 语句批量收集游标并循环更新插入到 STORE_INVENTORY 表中。 现在我想将 STORE_INVENTORY 表中不在游标中的 PRICE 列清空。 如何完成第 3 步?我已经可以执行第 1 步和第 2 步了,因为我已经更新或插入了从光标中拉出的项目。

以下是一些示例数据:

有三个源表由外部方更新。我的目标是获取这三个数据源并将其合并到一个单一的表中。

SOURCE TABLES

ITEM_TYPES
  DESC_ID | TYPE 
  A       |  Kitchen
  B       |  Bath

ITEM_MANIFEST

  LOC_ID  | ORIGIN
  U       | USA
  C       | CHINA

ITEM_PRICE

  ITEM_ID | PRICE | DESC_ID | LOC_ID | DATE
  0       | 3.99  |  A      |  U     | 9/11/2015
  1       | 2.99  |  B      |  C     | 9/11/2015
  2       | 1.99  |  A      |  U     | 9/05/2015

目的地表

STORE_INVENTORY 
  ITEM_ID | TYPE    | ORIGIN | PRICE
  0       | Kitchen | CHINA  | 3.99
  8       | Bath    | USA    | 2.99

所以在我执行了以日期为参数的 SQL 过程之后。如果它在给定日期之后,它只会从 ITEM_PRICE 中提取。

如果使用传入日期 9/10/2015 执行该过程

预期输出

STORE_INVENTORY
0 | Kitchen | USA   | 3.99
1 | Bath    | China | 2.99
8 | Bath    | USA   | NULL

【问题讨论】:

    标签: oracle plsql merge


    【解决方案1】:

    那么,是这样的吗?

    drop table item_description;
    drop table item_manifest;
    drop table item_price;
    drop table store_inventory;
    
    create table item_description 
    as
    select 'A' desc_id, 'Kitchen' type from dual union all
    select 'B' desc_id, 'Bath' type from dual;
    
    create table item_manifest
    as
    select 'U' loc_id, 'USA' origin from dual union all
    select 'C' loc_id, 'CHINA' origin from dual;
    
    create table item_price
    as
    select 0 item_id, 3.99 price, 'A' desc_id, 'U' loc_id, to_date('11/09/2015', 'dd/mm/yyyy') dt from dual union all
    select 1 item_id, 2.99 price, 'B' desc_id, 'C' loc_id, to_date('11/09/2015', 'dd/mm/yyyy') dt from dual union all
    select 2 item_id, 1.99 price, 'A' desc_id, 'U' loc_id, to_date('05/09/2015', 'dd/mm/yyyy') dt from dual;
    
    create table store_inventory
    as
    select 0 item_id, 'Kitchen' type, 'CHINA' origin, 3.99 price from dual union all
    select 8 item_id, 'Bath' type, 'USA' origin, 2.99 price from dual;
    
    select * from store_inventory;
    
       ITEM_ID TYPE    ORIGIN      PRICE
    ---------- ------- ------ ----------
             0 Kitchen CHINA        3.99
             8 Bath    USA          2.99
    
    select coalesce(ip.item_id, si.item_id) item_id,
                  coalesce(id.type, si.type) type,
                  coalesce(im.origin, si.origin) origin,
                  ip.price
           from   item_description id
                  inner join item_price ip on (id.desc_id = ip.desc_id and ip.dt > to_date('10/09/2015', 'dd/mm/yyyy')) -- use a parameter for the date here
                  inner join item_manifest im on (ip.loc_id = im.loc_id)
                  full outer join store_inventory si on (si.item_id = ip.item_id);
    
       ITEM_ID TYPE    ORIGIN      PRICE
    ---------- ------- ------ ----------
             0 Kitchen USA          3.99
             8 Bath    USA              
             1 Bath    CHINA        2.99
    
    merge into store_inventory tgt
    using (select coalesce(ip.item_id, si.item_id) item_id,
                  coalesce(id.type, si.type) type,
                  coalesce(im.origin, si.origin) origin,
                  ip.price
           from   item_description id
                  inner join item_price ip on (id.desc_id = ip.desc_id and ip.dt > to_date('10/09/2015', 'dd/mm/yyyy')) -- use a parameter for the date here
                  inner join item_manifest im on (ip.loc_id = im.loc_id)
                  full outer join store_inventory si on (si.item_id = ip.item_id)) src
      on (src.item_id = tgt.item_id)
    when matched then
      update set tgt.type = src.type,
                 tgt.origin = src.origin,
                 tgt.price = src.price
    when not matched then
      insert (tgt.item_id, tgt.type, tgt.origin, tgt.price)
      values (src.item_id, src.type, src.origin, src.price);
    
    commit;
    
    select * from store_inventory;
    
       ITEM_ID TYPE    ORIGIN      PRICE
    ---------- ------- ------ ----------
             0 Kitchen USA          3.99
             8 Bath    USA              
             1 Bath    CHINA        2.99
    

    显然,您的过程将有一个 DATE 数据类型的输入参数传递给查询,并且您的查询将使用该参数,而不是像我在示例中那样使用硬编码的日期。例如。 ip.dt > p_cutoff_date

    我已经可以执行第 1 步和第 2 步了,因为我已经更新或插入了 从光标中拉出的项目。

    嗯。这些步骤似乎没有必要——为什么不将它们作为 MERGE 语句的一部分呢?在您从游标进行插入/更新之前,store_inventory 表是什么样的?另外,您使用的光标是什么?

    【讨论】:

    • 哦,喜欢简单的示例表创建。我仍然认为整个事情比想象的要简单,但是没有更多的代码或关于从 OP 开始 STORE_INVENTORY 内容的详细信息,我不会被打扰。
    • 是的;整个“我正在使用光标”是一个重要的危险信号!基于集合的处理 FTW!
    【解决方案2】:

    在通过主连接将 TYPE 和 ORIGIN 拉入 ITEM_PRICE 后,您不能对 ITEM_PRICE.PRICE 进行有日期限制的子选择,而不限制日期吗?

    即类似的东西。

    select ITEM_ID, TYPE, ORIGIN
    /* not selecting PRICE in the main join */
    ,(select PRICE from ITEM_PRICE where your join conditions 
    and DATE >= your param)
    from ITEM_TYPES, ITEM_MANIFEST, ITEM_PRICE
    where your join conditions, but no criteria on DATE
    

    抱歉,如果您提供了现有的查询,输入起来会更清晰、更容易。

    通过重新阅读您的问题,我不确定您是否只插入 2 行但想要获得 3 行。或者如果您有 3 行,但您想将缺失的价格设为 NULL。

    如果目标表已经有 3 行,那么,与其使用基于 CURSOR 的方法(在大容量下可能会很慢并且编写起来很麻烦),为什么不使用 DATE 作为标准来进行 UPDATE 呢?如果不匹配,NULL 将分配给 price,这就是 UPDATE 的工作方式。

      UPDATE STORE_INVENTORY set PRICE
      = (select PRICE from ITEM_PRICE where your join conditions 
      and DATE >= your param)
    

    【讨论】:

      猜你喜欢
      • 2021-12-05
      • 2015-12-16
      • 2018-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-07
      • 2013-02-18
      • 1970-01-01
      相关资源
      最近更新 更多