2016-11-07 67 views
1

我试图使用窗函数来彼此接近(在同一分区中)转换成连续的组的组记录。可能有更好的方法来解决这个问题,但是现在我想尝试的是运行得太慢而无法使用。它涉及通过在选择的顺序:SQL索引相同的列两个方向遍历窗函数

order by person_id, rollup_class, rollup_concept_id, exp_num 

,另一次序由窗口函数:

lead(days_from_latest) over (partition by person_id, rollup_class, rollup_concept_id 
order by exp_num DESC) 

因为我有最后一列(exp_num)在相反的方向排列,查询需要永远。我甚至在桌面上有两个索引来处理这两个方向:

create index deeIdx on results.drug_exposure_extra (person_id,rollup_class, rollup_concept_id, 
    exp_num); 
create index deeIdx2 on results.drug_exposure_extra (person_id,rollup_class,rollup_concept_id, 
    exp_num desc); 

但是这没有帮助。所以我正在尝试在两个方向上订购exp_num的产品:

create index deeIdx3 on results.drug_exposure_extra (person_id,rollup_class,rollup_concept_id, 
    exp_num, exp_num desc); 

这样做是否合理?当指数最终完成建设,能否解决这个问题,我会回答我的问题...

都能跟得上。

即使所有三个指标,如果两个顺序停车处(在选择并在条款)去同一个方向,运行查询超级快,如果他们走相反的方向在查询运行超慢。所以,在这一点上,我想我应该更好地解释我的用例,并寻求更好的方法。

我有药物暴露记录(这是一个很酷的开源项目http://www.ohdsi.org/,顺便说一句),并且当一个人的药物暴露开始时间比从前任何暴露结束N天少,它应该是结合早先的那些到一个单一的'时代'。每当有超过N天的差距时,新的时代就会开始。

在撰写这个问题的过程中,原来我解决它。但是,它提出了一些有趣的问题,所以我会发布它并在下面回答。

回答

0

就像问医生,“当我移动我的胳膊像这样的疼,我该怎么办?”答案很明显,“不要这样移动你的手臂。”所以 - 不要试图使窗口函数按照与主查询不同的顺序进行(或者可能是相互的) - 这可能是一个更好的解决方案。

早在这方面的工作我不知怎么说服自己,这将是更容易相对于他们的结局记录,而不是他们的首发记录汇总的时代,但那是我哪里错了。

所以这给了我,我想时代数字表达式如下:

sum(case when exp_num = 1 or days_from_latest > 30 then 1 else 0 end) 
     over (partition by person_id, rollup_class, rollup_concept_id 
      order by exp_num) 
     as era_num 

说明:如果这是病人的第一次接触到药物(当然,rollup_class和rollup_concept_id在这种情况下,组合)那么这就是毒品时代的开始。如果曝光时间超过N天,这也是毒品时代的开始。 (这一点有点复杂:曝光1从第1天开始并且是60天,曝光2从第20天开始并且是10天,曝光3从第70天开始:它是在结束之后40天最近的曝光,2,这将把它放在一个新的时代,但它只是在曝光1后10天,这与1和2在同一个时代。)因此,对于每个开始一个时代的记录案例陈述给我们一个,剩下的就是0。然后我们总结一下,分区到我们在前面的查询中使用的同一个分区来建立exp_num,并按exp_num排序。我可以通过添加rows between unbounded preceding and current row来明确指定行数,但这是默认行为。因此,只有在新时代开始时,年代才会增加。

下面是一个简单的例子,以回应下面的戈登 - 莱诺夫的评论。

create table junk_numbers (x int); 
insert into junk_numbers values (1),(2),(3),(5),(7),(9),(10),(15),(20),(25),(26),(28),(30); 

-- break into series with gaps of at least 1 
select x, gap, 1+sum(case when gap > 1 then 1 else 0 end) over (order by x) as series_num 
from (
    select x, x - lag(x) over (order by x) as gap 
    from junk_numbers 
    ) as x_and_gaps 
order by x; 

    x | gap | series_num 
    ----+-----+------------ 
    1 |  |   1 
    2 | 1 |   1 
    3 | 1 |   1 
    5 | 2 |   2 
    7 | 2 |   3 
    9 | 2 |   4 
    10 | 1 |   4 
    15 | 5 |   5 
    20 | 5 |   6 
    25 | 5 |   7 
    26 | 1 |   7 
    28 | 2 |   8 
    30 | 2 |   9 

-- same query but bigger gaps: 
select x, gap, 1+sum(case when gap > 4 then 1 else 0 end) over (order by x) as series_num 
from (
    select x, x - lag(x) over (order by x) as gap 
    from junk_numbers 
    ) as x_and_gaps 
order by x; 


x | gap | series_num 
----+-----+------------ 
1 |  |   1 
2 | 1 |   1 
3 | 1 |   1 
5 | 2 |   1 
7 | 2 |   1 
9 | 2 |   1 
10 | 1 |   1 
15 | 5 |   2 
20 | 5 |   3 
25 | 5 |   4 
26 | 1 |   4 
28 | 2 |   4 
30 | 2 |   4 
+0

你应该问一下另一个关于样本数据和期望结果的问题。可能有其他解决方案来解决这个问题,您可能不会想到的解决方案。 –

+0

我不确定它是否值得另一个问题,但我只是汇集了数据,并将您的建议添加到我的答案中 - 从我的现实生活中的示例中简化了很多,但却捕获了问题。 – Sigfried