如何在Oracle中有效地将窄表转换为宽泛的?

时间:2015-07-02 08:31:47

标签: oracle

我有一个包含以下列的窄表: <Customer ID> <Field ID> <Value>,所有这些都是数字。

我想将此表重新整理为宽格式:

<Customer ID> <Field1> <Field2> <Field3> ...

我有一个单独的字典表DIC_FIELDS,它将字段ID转换为字段名称。

我在EXADATA服务器上工作。窄表有25亿条记录,我们有大约200个字段。

下面明显的简单解决方案严重填满了EXADATA服务器上的所有临时空间。

create table WIDE_ADS as (
   CUSTOMERID
  ,max(case when FIELDID = 1 then VALUE end) as GENDER
  ,max(case when FIELDID = 2 then VALUE end) as AGE
  ,max(case when FIELDID = 3 then VALUE end) as EDUCATION
from NARROW_ADS
group by CUSTOMERID
);

我们还尝试了一种更聪明且手动的方法:

create index index1
      on SZEROKI_ADS(CUSTOMERID);


DECLARE
    rowidWide rowid;
    type tColNames is table of STRING(32000) index by pls_integer ;
    arrColNames tColNames;
    x_CustomerID number;
    strColName varchar2(32);
    strColvalue varchar2(32000);
    strSQL varchar2(200);
    lngCounter pls_integer;
    lngFieldID pls_integer;
BEGIN
    lngCounter := 0;
    -- we pre-load the dictionary arrColNames to speedup lookup.
    for DIC_EL in (select * from DIC_FIELDS order by FIELDID) LOOP
        lngFieldID := to_number(DIC_EL.FIELDID);
        arrColNames(lngFieldID) := DIC_EL.FIELDNAME;
    END LOOP;

    FOR NARROW_REC IN (SELECT * FROM NARROW_ADS where VALUE is not null ) LOOP 
        strColName := arrColNames(NARROW_REC.FIELDID);
        strColvalue := NARROW_REC.VALUE;
        x_IDKlienta := NARROW_REC.CUSTOMERID;
        BEGIN
            select rowid into rowidWide from WIDE_ADS
                where CUSTOMERID = NARROW_REC.CUSTOMERID;
            strSQL := 'update :1 set :2 = :3 where rowid = :4';
            execute immediate strSQL using WIDE_ADS, strColName, strColvalue, rowidWide;
        EXCEPTION
        WHEN NO_DATA_FOUND THEN
                strSQL := 
                'insert into '|| WIDE_ADS ||' (CUSTOMERID, '|| strColName ||')
                 values 
                    (:1, :2)';
                execute immediate strSQL using  x_CustomerID, to_number(strColvalue) ;
        END;
        IF lngCounter=10000 THEN
            COMMIT;
            lngCounter:=0;
            dbms_output.put_line('Clik...');
        ELSE
            lngCounter:=lngCounter+1;
        END IF;    
    END LOOP;
END;

虽然它不需要温度,但它在性能方面却失败了;它在50秒内处理10 000条记录 - 比预期的慢1000倍。

我们可以做些什么来加快这个过程?

1 个答案:

答案 0 :(得分:1)

正如Lalit所述,请尝试以CUSTOMERID为基础的块进行。

首先,在CUSTOMERID上创建一个索引(如果它不存在):

CREATE INDEX INDNARROWADS ON NARROW_ADS(CUSTOMERID);

其次,我们将创建一个辅助表来计算基于CUSTOMERID的桶(在本例中我们创建1000个桶,1个桶代表1个块插入语句):

CREATE TABLE BUCKETS(MINCUSTOMER, MAXCUSTOMER, BUCKETNUM) AS
  SELECT MIN(CUSTOMERID), MAX(CUSTOMERID), BUCKET
    FROM (SELECT CUSTOMERID, 
                 WIDTH_BUCKET(CUSTOMERID, 
                              (SELECT MIN(CUSTOMERID) FROM NARROW_ADS),
                              (SELECT MAX(CUSTOMERID) FROM NARROW_ADS),
                              1000) BUCKET
            FROM NARROW_ADS)
    GROUP BY BUCKET;

您可以使用更多/更少的存储桶来修改WIDTH_BUCKET函数的第四个参数。

第三,创建WIDE_ADS表(没有数据的结构)。您应该手动执行此操作(特别注意存储参数),但您也可以使用WHERE错误条件的自己的查询:

create table WIDE_ADS as select
   CUSTOMERID
  ,max(case when FIELDID = 1 then VALUE end) as GENDER
  ,max(case when FIELDID = 2 then VALUE end) as AGE
  ,max(case when FIELDID = 3 then VALUE end) as EDUCATION
 from NARROW_ADS
 where 1=0;

第四,对每个桶执行查询(1个桶意味着1个插入语句):

BEGIN
  FOR B IN (SELECT * FROM BUCKETS ORDER BY BUCKETNUM) LOOP
     INSERT INTO WIDE_ADS  
       SELECT 
         CUSTOMERID
        ,max(case when FIELDID = 1 then VALUE end) as GENDER
        ,max(case when FIELDID = 2 then VALUE end) as AGE
        ,max(case when FIELDID = 3 then VALUE end) as EDUCATION
       FROM NARROW_ADS
       WHERE CUSTOMERID BETWEEN B.MINCUSTOMER AND B.MAXCUSTOMER
       GROUP by CUSTOMERID;
     COMMIT;
  END LOOP;
END;

最后,删除辅助表(如果没有必要,则删除索引)。

Oracle优化器应使用CUSTOMERID上的索引对NARROW_ADS执行“索引范围扫描”。因此,每个INSERT应该有效地找到相应的间隔。

请注意,WIDTH_BUCKETS根据CUSTOMERID上指定时间间隔内的统一划分创建存储区(从最小值到最大值)。它不会根据统一的行数创建存储桶。另请注意,在执行此过程时,不得修改NARROW_ADS

当PL / SQL块在每次迭代时执行COMMIT并且循环使用BUCKETNUM顺序迭代桶时,您可以看到WIDE_ADS如何增长以及正在处理哪个桶(检索最大值)来自CUSTOMERID的{​​{1}}并在WIDE_ADS表上找到相应的存储分区。

如果临时空间使用率很高,则增加桶数(每个插入量都会更小)。

相关问题