【问题标题】:SQL, ORACLE: Update the value of a column with different valuesSQL、ORACLE:用不同的值更新列的值
【发布时间】:2016-08-16 12:57:57
【问题描述】:

我搜索了很多,但找不到我的问题的答案。

所以,我必须在不同的表中生成大量数据(数百万行),并且这个脚本应该很快。 现在让我们谈谈 3 个特定的表,其中有 id 行:

  • 在表Owners中,id从10000开始,递增1(有序列)
  • 在表 Cars 中,id 从 10000000 开始,递增 1(有序列)。


在名为 Ownership 的第三个表中,我必须将这些 ID 与某些特定比率“合并”:

  • 50% 的车主拥有一辆车
  • 其余 20% 的车主拥有 2 辆车
  • 其余 10-10-10% 将拥有 3、4 和 5 辆汽车


重要的事情:

  • OwnersCars 表的注意事项:费率:对于 100 个单元的所有者,将生成 210 个单元的汽车,并且在 Ownership 表中汽车将是独一无二的,因此该表中也将包含 210 行
  • 首先是Owners,然后是Cars
  • 然后我在 Ownership 表中插入一些“来自”Cars 的值



注意:这是我在 Cars 中生成行的方式(Owners 类似)
(它将生成 v_custom_unit 件行,这个单位是根据我给出的费率计算得出的几行前(对于 100 个单元所有者,将生成 210 个单元汽车和 210 个单元所有权),然后我使用 for 循环来乘以行):

insert /*+ APPEND */ into Cars(
    carId
  , carType
  , ...
)
select /*+ PARALLEL */
    seq_carid.nextval as carId
  , REGEXP_SUBSTR( 'Suziki,Toyota,Subaru,Saab,Hyundai,Opel,Volkswagen', '([^,]+)', 1, ROUND(DBMS_RANDOM.VALUE(1,7)) ) as carType
  , ...
from dual
connect by level <= v_custom_unit;

乘法是这样的:

FOR i in 1..v_forSteps LOOP

    EXECUTE IMMEDIATE '
      insert /*+ APPEND */ into Cars (
          carId,
        , carType,
        , ...
      )
      SELECT /*+ PARALLEL */
          seq_carid.nextval as carId,
        , carType
        , ...
      FROM Cars
      WHERE ROWNUM <= ' || v_custom_unit;

      COMMIT;

END LOOP;

下一步是生成所有权行:

insert /*+ APPEND */ into Ownership (
     ownerId
   , carId
   , date_bought
)
select /*+ PARALLEL */
     1
   , c.carId
   , some_random_date as date_bought
from Cars c;

我的问题来了:每辆车都在 OwnerId=1 的所有权中。

我的问题是:如何使用不同的所有者值更新 Ownership 表在一次更新中(并且可能保持利率 (50%-20%-10%-10%-10%))?

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    其中一种方法:

    insert into ownership (ownerid, carid, bought)
      with t(oid, cid, cnt) as ( 
        select 1, 1, 1 from dual
        union all 
        select case when oid <= 5 
                         or oid <=  7 and cnt >= 2 
                         or oid <=  8 and cnt >= 3 
                         or oid <=  9 and cnt >= 4 
                         or oid <= 10 and cnt >= 5 then oid + 1
                    else oid 
               end, 
               cid + 1, 
               case when oid <=  5 
                         or oid <=  7 and cnt >= 2 
                         or oid <=  8 and cnt >= 3 
                         or oid <=  9 and cnt >= 4 
                         or oid <= 10 and cnt >= 5 then 1
                    else cnt + 1 
               end
        from t where cid < 21) 
      select oid, cid, trunc(sysdate) - round(dbms_random.value * 1000) from t
    

    这是 10 位车主和 21 辆汽车的演示。不确定效率,但这只是一个递归查询。需要 Oracle 11g。

    测试表和输出:

    create table ownership (carid number(6), ownerid number(6), bought date);
    
      CARID OWNERID BOUGHT
    ------- ------- -----------
          1       1 2013-12-29 -- one car
          2       2 2015-12-16
          3       3 2014-04-04
          4       4 2013-12-17
          5       5 2013-11-20
          6       6 2014-04-04 -- two cars
          7       6 2015-09-05
          8       7 2013-12-19
          9       7 2016-01-02
         10       8 2015-08-22 -- three 
         11       8 2014-03-05
         12       8 2016-07-14
         13       9 2015-09-02 -- four 
         14       9 2015-08-28
         15       9 2015-06-04
         16       9 2014-04-20
         17      10 2016-08-07 -- five 
         18      10 2015-07-16
         19      10 2014-12-08
         20      10 2016-04-26
         21      10 2014-05-30
    

    【讨论】:

    • 感谢您的回答!不幸的是,在我发布使数据生成更容易的问题后,我得到了一个新信息,但我会接受这个作为答案,因为它很有用,而且我必须使用查询中的数字来生成更多行。如果您(以及其他任何人)对此感兴趣,我很快就会分享我的解决方案。
    【解决方案2】:

    如果您只需要用测试数据填充空表并且性能很重要,您可以考虑将 dml 和序列放在一边。为了保持利率,您可以在余数的基础上使用函数ownerId = f(carId)

    --drop table owners;
    --drop table cars;
    --drop table ownership;
    
    create table owners as
      select level n
        from dual
        connect by level < 1000*1000*1;
    
    create table cars as
      select level n
        from dual
        connect by level < 2100*1000*1;
    
    create table ownership as
      select level cid,
             10*trunc(level/21) + case
                                    when mod(level, 21) between 0  and  4 then mod(level, 21)
                                    when mod(level, 21) between 5  and  6 then 5
                                    when mod(level, 21) between 7  and  8 then 6
                                    when mod(level, 21) between 9  and 11 then 7
                                    when mod(level, 21) between 12 and 15 then 8
                                    when mod(level, 21) between 16 and 20 then 9
                                  end oid
        from dual
        connect by level < 2100*1000*1;
    

    检查结果:

    with
      cars_by_owner as (
        select oid, count(*) cnt
          from ownership 
          group by oid),
      owners_by_cars_count as (
        select cnt, count(*) c, grouping(cnt) rg
          from cars_by_owner
          group by rollup(cnt))
    select f.cnt "cars count",
           f.c "owners count",
           round(f.c/s.c*100) "%"
      from owners_by_cars_count f
      join owners_by_cars_count s
        on f.rg = 0 and s.rg = 1
    
    cars_count  owners_count  %
        1       499999        50
        2       200000        20
        3       100000        10
        4       100000        10
        5       100000        10
    

    创建 100 万车主、210 万辆汽车并填满 ownership 表需要几秒钟。

    如果您遇到 ORA-30009,您可以使用正数生成帮助表,而不是使用 connect by level

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-18
      • 1970-01-01
      相关资源
      最近更新 更多