我使用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秒,并且不使用任何索引。
所以我的问题是:
如何改进查询,和/或如何使用索引来确保自联接可以使用索引?
提前致谢。
答案 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不为空的行。