如何索引自我加入

时间:2016-01-13 11:15:30

标签: sql indexing sas

我使用SAS University Edition分析下表(实际上有2.5M行)

p_id c_id startyear endyear
0001 3201      2008    2013
0001 2131      2013    2015
0013 3201      2006    2010

其中p_id是person_id,c_id是companyid。

我希望在某一年内获得一些同事(在同一公司的重叠范围内工作的人数),因此我创建了一个包含不同p_ids的表并执行以下查询:

PROC SQL;

UPDATE no_colleagues AS t1
SET c2007 = (
    SELECT COUNT(DISTINCT t2.p_id) - 1
    FROM table AS t2
    INNER JOIN table AS t3
    ON  t3.p_id = t1.p_id
    AND t3.c_id = t2.c_id
    AND t3.startyear <= t2.endyear   % checks overlapping criteria
    AND t3.endyear >= t2.startyear   % checks overlapping criteria
    AND t3.startyear <= 2007         % limits number of returns
    AND t2.startyear <= 2007         % limits number of returns
);

对索引查询(p_id,c_id,startyear,endyear)的单个查找需要0.04秒。单个更新上面的查询大约需要1.8秒,并且不使用任何索引。

所以我的问题是:

如何改进查询,和/或如何使用索引来确保自联接可以使用索引?

提前致谢。

2 个答案:

答案 0 :(得分:1)

根据您的数据,我会做这样的事情,但您可能需要调整代码以满足您的需求。

首先,创建一个包含p_id,c_id,year的表。 所以你在公司3201工作的第一个人将在这个表中有6个观察,每个工作年一个。

data have_count;
    set have;

do i=startyear to endyear;
    worked_in = i;
    output;
end;

drop i startyear endyear;
run;

现在你只需数数和agreggate:

proc sql;
select
    worked_in as year
    ,c_id
    ,count(distinct p_id) as no_colleagues
from have_count
group by 1,2;
quit;

结果:

year c_id no_colleagues 
2006 3201 1 
2007 3201 1 
2008 3201 2 
2009 3201 2 
2010 3201 2 
2011 3201 1 
2012 3201 1 
2013 2131 1 
2013 3201 1 
2014 2131 1 
2015 2131 1 

答案 1 :(得分:0)

更有效的方法:

1)为结果而不是宽格式创建长格式表。这将更容易填充,以后更容易使用。

create table colleagues_by_year (
    p_id int,
    year int,
    colleagues int
);

现在可以使用单个insert语句填充此内容。唯一的诀窍是在决赛桌中获得您想要的完整年份列表。有几个选项,但由于我不太熟悉SAS SQL,我将使用一个非常简单的选项:多年的查找表,您可以加入。

create table years (
   year int
);

insert into years 
   values (2007),(2008),...

(更复杂的方法是递归查询,它可以找到输入数据中所有年份的范围。)

现在最后插入:

insert into colleagues_by_year
    select p_id,
           year,
           count(*)
       from colleagues
       join years on
          years.year between colleagues.startyear and colleagues.endyear
       group by p_id,year

这将没有任何行,其中年份的同事数量为0.如果您希望这样,您可以将年份变为左连接,并且仅计算years.year不为空的行。