PROC SQL更新大数据集的效率

时间:2015-06-08 11:44:25

标签: sql performance sas large-data

我有一个拥有1000万行和1800列的SAS Master数据集。我需要使用具有1500万条记录的事务数据集更新10列,仅用于具有匹配键的记录。 我尝试使用以下代码运行proc sql update语句。

proc sql;
UPDATE lib1.master1 a

SET col1 = (SELECT col1 FROM lib1.changes_1 b WHERE a.key=b.key),
    col2 = (SELECT col2 FROM lib1.changes_1 b WHERE a.key=b.key),
    col3 = (SELECT col3 FROM lib1.changes_1 b WHERE a.key=b.key),
    col4 = (SELECT col4 FROM lib1.changes_1 b WHERE a.key=b.key),
    col5 = (SELECT col5 FROM lib1.changes_1 b WHERE a.key=b.key),
    col6 = (SELECT col6 FROM lib1.changes_1 b WHERE a.key=b.key),
    col7 = (SELECT col7 FROM lib1.changes_1 b WHERE a.key=b.key),
    col8 = (SELECT col8 FROM lib1.changes_1 b WHERE a.key=b.key),
    col9 = (SELECT col9 FROM lib1.changes_1 b WHERE a.key=b.key)

WHERE EXISTS ( SELECT 1 FROM lib1.changes_1 b WHERE A.key = B.key);
quit;

我尝试将col1用于测试目的,并且已运行超过4小时。

我可以通过删除10列然后左连接来考虑数据合并,但这会改变列排序。重新排序1800列将再次成为一项繁琐的工作。

是否有更快/更有效的技术?

4 个答案:

答案 0 :(得分:4)

要替换一列,格式(大致类似于Bendy的方法)最简单。

要替换十列,总是来自同一行,我建议使用哈希表。通常,与单一格式的速度大致相同。 (在10MM行标记处,格式实际上可能有点慢,所以这甚至可能比一个更快。)

我的笔记本电脑需要大约30秒(CPU时间和实时;我有一个SSD,所以它们很相似。在硬盘上这可能是30秒CPU时间和几分钟实时。)

*make some dummy data;
data maindata;
  array col(10);
  do _i = 1 to dim(col);
    col[_i] = _i;
  end;
  do key = 1 to 1e7;
    output;
  end;
run;
data updatedata;
  array col(10);
  do _i = 1 to dim(col);
    col[_i] = 100+_i;
  end;
  do key = 1 to 3e7 by 2;
    output;
  end;
run;

*using MODIFY here which is identical in function to SQL UPDATE - does not make a new dataset;
data maindata;  
  if _n_=1 then do;
    declare hash ud(dataset:'updatedata');
    ud.defineKey('key');
    ud.defineData('col1', 'col2', 'col3', 'col4', 'col5', 'col6', 'col7', 'col8', 'col9', 'col10');
    ud.defineDone();
  end;
  modify maindata;
  rcUpdate = ud.find();
  if rcUpdate=0 then replace;
run;

答案 1 :(得分:0)

你有挑战。首先,我建议在lib1.changes_1(key)上创建一个索引,如果你没有索引的话。这可能是一个很大的性能提升。

proc sql在更新中不支持join - 这正是您真正需要的。但是,许多底层数据引擎都可以。因此,如果要与数据库(例如MySQL,Postgres或SQL Server)中的数据进行通信,则可以编写本机模式查询以使用join进行更新。

全SAS解决方案将使用数据步骤。

答案 2 :(得分:0)

如何为事务数据集B中的10列中的每一列生成格式?

生成虚拟数据集:master(10M记录)和transaction(15M记录)数据集: (原始的transaction数据集在下面标准化,以准备创建每个事务变量的格式)

data master ;
  do key=1 to 10000000 ;
    output ;
  end ;
run ;

data transaction(keep=start label fmtname) ;
  array t(10) col1-col10 ;
      do i=1 to 10 ;
        do start=1 to 15000000 ;
        fmtname=cat('COL',i,'F') ;
        label=cat('Column ',i,' has value ',start) ;
        output ;
    end ;
  end ;
run ;

以格式读取:

proc format cntlin=transaction ;
run ;

然后将格式应用于master数据集:

data want ;
  set master ;
    col1=put(key,COL1F.) ;
    col2=put(key,COL2F.) ;
    col3=put(key,COL3F.) ;
    col4=put(key,COL4F.) ;
    col5=put(key,COL5F.) ;
    col6=put(key,COL6F.) ;
    col7=put(key,COL7F.) ;
    col8=put(key,COL8F.) ;
    col9=put(key,COL9F.) ;
    col10=put(key,COL10F.) ;
    output ;
run ;

答案 3 :(得分:0)

摘要

  1. 汇总lib1.changes_1到执行完整扫描的临时表(例如,lib1.changes_1_rolledup)。 (lib1.changes_1_rolledup的物理属性应该仔细设置。)
  2. 使用lib1.master1的数据更新lib1.changes_1_rolledup,对其中一个进行全扫描,另一个进行索引扫描。 (全扫描哪一个取决于实际数据。)
  3. 解释

    首先,为了获得更好的性能,您很可能必须深入到底层DBMS级别并利用其功能。

    然后,优化技术实际上取决于数据的性质。

    我认为[几乎]所有lib1.changes_1(key)值都匹配lib1.master1(key)个值中的一个(lib1.changes_1甚至可能是lib1.master1的详细信息表)。此外,我们需要应用lib1.changes_1的所有更改。这意味着我们必须从lib1.changes_1读取所有记录。如果是这样,最有效的方法是lib1.changes_1完全扫描,但只执行一次。此完整扫描会将lib1.changes_1中的所有更改汇总到此类定义的(可能的临时)表中:

    -- pseudo code:
    create [temporary] table lib1.changes_1_rolledup 
      <set physical attributes depending on your data nature - see below>
      as select key, col1, col2, col3, col4, col5, col6, col7, col8, col9
      from lib1.changes_1
      where 1 = 2
    

    此汇总更改表可能包含不超过10M的记录,并且根据存储空间要求,colX大小可能相对较小。更重要的是,每key值只有一条记录,这可能是一个很大的好处。

    我们需要评估在lib1.master1中表示lib1.changes_1_rolledup个记录的数量,如果主 - 细节关系只是记录计数的比较。

    如果lib1.changes_1_rolledup如果只有两个树时间(速率实际上取决于DBMS)或者比lib1.master1更短(就记录数而言),那么最有效的方法就是执行全面扫描lib1.master1使用lib1.master1中的相应值更新colX(一次全部9 lib1.changes_1_rolledup个值)的每条记录。 (当然,只要有可能,就应该在单个更新查询中实现完全扫描的更新。)在这种情况下,必须调整lib1.changes_1_rolledup表的物理属性以进行密钥查找。我建议的技术类似于Oracle的索引组织表(如果可用)。

    如果lib1.changes_1_rolleduplib1.master1短几倍,那么从lib1.changes_1_rolledup更新相应记录的效率会更高lib1.master1。在这种情况下,lib1.changes_1_rolledup的物理存储应该调整为完全扫描,并且可能是lib1.master1(key)外观的顺序(例如,自动递增的代理键可能就是这种情况)。

    P.S。 为了简化描述,我省略了lib1.changes_1仅包含某个键的部分列更新的情况。这可以通过在lib1.changes_1_rolledup中添加标志字段并按如下方式调整更新来处理:

    -- pseudo code:
    update lib1.master1 m
      set m.col1 = (
            select case c.col1 then select c.col1 else m.col1 end
              from lib1.changes_1_rolledup c
              where c.key = m.key
          )
      ..............