我有一个拥有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列将再次成为一项繁琐的工作。
是否有更快/更有效的技术?
答案 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)
lib1.changes_1
到执行完整扫描的临时表(例如,lib1.changes_1_rolledup
)。 (lib1.changes_1_rolledup
的物理属性应该仔细设置。)lib1.master1
的数据更新lib1.changes_1_rolledup
,对其中一个进行全扫描,另一个进行索引扫描。 (全扫描哪一个取决于实际数据。)首先,为了获得更好的性能,您很可能必须深入到底层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_rolledup
比lib1.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
)
..............