【问题标题】:Optimize SAS Proc SQL query优化 SAS Proc SQL 查询
【发布时间】:2015-12-15 11:43:47
【问题描述】:

我有 2 个大表,我正在尝试加入这些表,以便根据第二个表中的字段对第一个记录进行分组。左表有大约 5000 万条事件记录,右表有大约 3500 万条每月间隔记录。每月间隔处于 subjID 级别,因此我无法通过仅保留开始日期和结束日期来减小右表的大小。目前执行连接大约需要 40 - 60 分钟。

我尝试在 subjID、eventDate、startDate 和 endDate 上创建简单的索引,但是它似乎并没有提高性能(创建索引在大约 5 分钟内完成,连接在 38 分钟内完成)。

我可以使用其他选项来改进处理吗?

subjID 级别的事件左表:

data eventsTable;
input @1 subjID 8.
    @10 eventDate date9.;
format eventDate mmddyy10.;
datalines;
101      01AUG2011
101      28AUG2011
101      30AUG2011
101      01SEP2011
101      12SEP2011
101      28SEP2011
102      01JAN2015
102      15JAN2015
102      01FEB2015
102      16FEB2015
;
run;

subjID 级别的月间隔右表。如果事件发生在开始日期和结束日期之间,我会尝试将 endDate 带到事件中:

data monthlyTable;
input @1 subjID 8.
    @10 startDate date9. 
    @22 endDate date9.;
format startDate endDate mmddyy10.;
datalines;
101      28JUL2011   30AUG2011
101      30AUG2011   28SEP2011
101      28SEP2011   28OCT2011
102      01DEC2014   02JAN2015
102      02JAN2015   02FEB2015
102      02FEB2015   02MAR2015
;
run;

输出:

proc sql;
create table wantTable as 
    select a.*,
        endDate as monthlyDate
    from eventsTable a left join monthlyTable b on 
        a.subjID = b.subjID
    where a.eventDate > b.startDate and a.eventDate <= b.endDate
        order by subjID, eventDate;
quit;

【问题讨论】:

  • 您是只是从monthlyTable 附加enddate 还是附加一些其他变量?
  • 我从monthlyTable 中附加5 个变量。在实践中,我的左表有 6 个变量,我的右表有 11 个变量。
  • 我已经更新了我的答案,加入了一种哈希方法,可以让您一次性附加所有变量。

标签: sql join optimization sas proc


【解决方案1】:

如果您有足够的内存并且只需要来自monthlyTableenddate,您可能会发现格式合并是一种更有效的方法。但是,如果两个数据集都很大,那么您可以期望的优化只有这么多,因为您总是必须至少完整读取每个数据集。

data t_format(keep = fmtname--hlo) /view = t_format;
  set monthlytable(keep = subjID startdate enddate) end = eof;
  retain fmtname 'myinfmt' type 'i';
  length start end $18; /*Increase for IDs longer than 8 digits*/
  start = cats(put(subjID,z8.),put(startdate + 1,yymmdd10.));
  end   = cats(put(subjID,z8.),put(enddate,yymmdd10.));
  label = enddate;
  output;
  if eof then do;
    hlo = 'O';
    label = .N;
    output;
  end;
run;

proc format cntlin = t_format;
run;

data want;
  set eventstable;
  enddate = input(cats(put(subjID,z8.),put(eventdate,yymmdd10.)),myinfmt18.);
  format enddate yymmdd10.;
run;

注意yymmdd10.z8. 格式的使用——这些格式确保键的长度始终相同,避免歧义,并且在创建数字信息@987654326 时以升序正确指定查找值的范围@。我想,严格来说,这是一个 informat 合并而不是 format 合并,但这是同一种想法。

如果您想通过这种方法返回多个查找变量,则需要在定义格式时将它们连接在一起,然后在应用后将它们拆分。

我估计这种方法需要大约 1.5GB 的内存来存储您指定的数据集 - 即(18 字节 x 2 每个日期范围 + 8 字节用于格式化值)x 35m 行。根据您的 ID 长度,这可能会有所不同。

如果您需要多个查找值,那么您可以使用哈希合并来做类似的事情,但我怀疑在这种情况下格式合并更有效。

一种可能的哈希合并方法如下所示:

data t_lookup /view= t_lookup;
  set monthlytable;
  by subjID;
  if first.subjID then id_range_count = 0;
  id_range_count + 1;
run;

data want;
  set eventstable;
  if _n_ = 1 then do;
    if 0 then set monthlytable(keep = subjID startdate enddate); /*Add extra lookup vars here as needed*/
    declare hash h(dataset:"t_lookup");
    rc = h.definekey("subjID","id_range_count");
    rc = h.definedata("startdate","enddate"); /*Add extra lookup vars here as needed*/
    rc = h.definedone();
  end;
  match = 0;
  rc    = 0;
  do id_range_count = 1 by 1 while(rc = 0 and match = 0);
    rc = h.find();
    match = startdate < eventdate <= enddate;
  end;
  if match = 0 then call missing(startdate,enddate);
  drop rc match id_range_count;
run;

【讨论】:

  • 我使用每个建议的方法运行同一组数据 6 次:Proc SQL、Hash Merge 和 Format Merge,Format Merge 产生了稍微好一点的结果。我多次运行它以获得更具代表性的集合。每种方法的平均值如下(h:mm:ss): Proc SQL = 0:19:21;哈希合并 = 0:21:58;格式合并 = 0:16:11。 40 分钟的初步结果追溯到服务器上的问题,因此它不是具有代表性的运行。
【解决方案2】:

查询的最佳索引是monthlyTable(subjId, startDate, endDate) 上的复合索引。不过,我不确定这是否会对 SAS 的性能有很大的改进。

【讨论】:

  • 我尝试使用复合索引,但是我认为 SAS 根本没有使用我创建的索引。我在wantTable 查询的末尾有一个排序,根据日志,我认为 SAS 正在使用它。 NOTE: SAS threaded sort was used.
【解决方案3】:

与创建索引相比,我对数据集进行预排序的运气更好。但是,预排序可能需要很长时间,具体取决于数据集的大小以及您对它们进行排序的内容。它可能需要比原始 SQL 查询更长的时间,因此测试变得很重要。

尝试运行

PROC SORT DATA=eventsTable ;
  BY subjID eventDate ;
RUN ;

PROC SORT DATA=monthlyTable ;
  BY subjID startDate endDate ;
RUN ;

在您的 PROC SQL 之前。我唯一的解释是 SAS 识别 SORT BY 标头信息并且不需要扫描整个表以查找连接,因为给定的 subjID 可能只会在几个连续的页面上。在几个连续的页面上也会减少 I/O。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 2013-01-10
    • 2014-02-04
    相关资源
    最近更新 更多