【问题标题】:Quickly retrieving the last row from a SAS index从 SAS 索引中快速检索最后一行
【发布时间】:2014-04-23 18:42:39
【问题描述】:

我有一个非常大的表,其中包含一个 ID 字段和一个日期时间字段。该表按 ID 字段排序,并在日期时间字段上进行索引。

我想快速找到最大日期时间值,但找不到任何好方法。

样本数据:

data x;
  do id=1 to 10000000;
    created_datetime = datetime() + (ranuni(1)*100000);
    output;
  end;
  format created_datetime datetime22.;
run;

proc sql noprint;
  create index created_datetime on x;
quit;

尝试 #1:PROC SQLmax() 函数

出于某种原因,我认为这会立即返回结果,但我发现实际发生的事情是违反直觉的(至少对我而言)。使用 max() 函数不使用索引 - 它不能! Where 子句等可以使用索引,但 max() 函数不能。即使你强制使用索引,它仍然会处理表中的所有行,只是按照使用索引返回它们的顺序。

option msglevel=i;
proc sql noprint;
  select max(created_datetime) from x(idxname=x);
quit;

尝试 #2:按组处理

下面使用索引轻松返回第一行:

data min; 
  set x;
  by created_datetime;
  output;
  stop;
run;

但是我不能使用descending关键字在列表中反向工作以获取最后一行:

data min; 
  set x;
  by descending created_datetime;
  output;
  stop;
run;

SAS 似乎也不支持降序索引,所以我也不能使用这种方法。

尝试 #3:使用有关索引的元数据和 WHERE 声明

我查看了SASHELP.VINDEX,希望最大值可能存储在元数据中的某个地方,然后我可以在 where 语句中使用。运气不好。

编辑:

尝试 #4:PROC SQLinobsoutobs

@DomPazz 的以下回答启发我重新审视其他一些基于 SQL 的解决方案。我认为PROC SQL 中的order by 语句可能会与 inobs 或 outobs 选项交互以实现我的目标。但它没有用。排序看起来像是应用于查询的输出,绝不会影响实际读入行的顺序。

/* Uncomment options as necessary */
proc sql noprint /*inobs=1 outobs=1*/;
  create table temp as 
  select created_datetime
  from x
  order by created_datetime desc;
quit;

帮助!

【问题讨论】:

  • 这个日期时间字段的连续性如何?您基本上有单独的索引记录,还是日期时间真的存储了更大的东西并且每个相同的日期时间值有很多记录?
  • 对于给定的日期时间,可能有多个记录 - 甚至可以达到毫秒。
  • 有可能,但正常吗?
  • 是的,它经常发生。我通常不会查看毫秒值,因为在将数据导入 SAS 时我会截断它们。对于我们如何使用数据,我们不需要那种粒度级别。
  • 嗯,我想。我通常会有一个不同的表,只存储 datetime 和 eventID 或类似的东西,这样会更容易。

标签: indexing sas


【解决方案1】:

这会给你百分位 - 最后一个应该是 100% 标记。这要求在对数据进行任何添加/删除后使用UPDATECENTILES 选项重新创建索引。

proc contents data=have centiles;
run;

如果您希望将其作为数据集(输出表名称为“INDEXES”),您可以使用 ODS OUTPUT 获取它:

ods output indexes=temp;
proc contents data=have centiles ;
run;

请参阅 Michael Raithel 的论文,尤其是 The Basics Of Using SAS Indexes,了解更多详情。

【讨论】:

  • 根据我刚刚运行的测试,您可能必须专门更新百分位数。我使用 95% 的百分位值尝试了上述操作,SAS 回答说它没有使用索引,因为数据没有排序。
  • 您能否在您的示例中的 where 子句中针对原始数据集使用来自 temp 的 max(datetime) 值来验证最大值?这次MAX 将利用索引,应该比全表MAX 更快。
  • 不幸的是@mjsqu 它似乎没有那样工作。我进行了类似的测试,由于数据集未排序,它仍然与所有表的最大值相当。
  • 耻辱@Joe,我记得找到了一种使用CONTENTS 来加速MAX 与索引SPD 表的好方法,但这不适用于这里。
【解决方案2】:

我能做的 - 因为它是一个日期时间值,所以创建一个包含相同值乘以 -1 的新字段,然后索引新字段。丑陋,但它会工作。

优点:-

  • 解决了这个问题。
  • 简单 - 用一些 cmets 很容易解释

缺点:-

  • 额外的字段和索引会浪费空间。
  • 与维护索引相关的额外处理开销。
  • 仅适用于保证 >= 0 的数字字段
  • 这是一个丑陋的黑客攻击
  • 可能还有很多其他人......

除非有人能想到更好的方法,否则我可能最终会采用这种方法。

【讨论】:

    【解决方案3】:

    Distinct() 使用索引。在您的示例中,这比您的其他方法慢,但对于一个非常大的表,它可能会更快。

    proc sql noprint;
    create table temp as 
        select distinct(created_datetime) as max
        from x
        order by max desc;
    quit;
    
    18003  proc sql noprint;
    18004  create table temp as
    18005      select distinct(created_datetime) as max
    18006      from x
    18007      order by max desc;
    INFO: Index created_datetime of SQL table WORK.X selected for SQL SELECT DISTINCT/UNIQUE
          optimization.
    NOTE: SAS threaded sort was used.
    NOTE: Table WORK.TEMP created, with 9999865 rows and 1 columns.
    
    18008  quit;
    NOTE: PROCEDURE SQL used (Total process time):
          real time           2.97 seconds
          cpu time            4.54 seconds
    

    【讨论】:

    • 谢谢多姆。此解决方案对我当前的问题不实用,但您的 SQL 方法启发了我的以下答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多