如何查询另一个表中的上一条记录?

时间:2016-09-24 01:41:43

标签: sql sql-server join sql-server-2012 sql-view

我的视图显示如下内容:

查看大众

| ID |     DT     | VAL|
|----|------------|----|
|  1 | 2016-09-01 |  7 |
|  2 | 2016-08-01 |  5 |
|  3 | 2016-07-01 |  8 |

我有一张历史日期的表格,如下所示:

表HIST

| ID |     DT     | VAL|
|----|------------|----|
|  1 | 2016-06-27 |  4 |
|  1 | 2016-06-29 |  3 |
|  1 | 2016-07-15 |  0 |
|  1 | 2016-09-12 |  8 |
|  2 | 2016-05-05 |  3 |

我需要的是在我的视图中添加另一列,其中包含一个布尔值,表示“历史记录中存在前一条记录并且其相关值大于零”。

预期输出如下:

| ID |     DT     | VAL| FLAG |
|----|------------|----|------|
|  1 | 2016-09-01 |  7 | false| -- previous is '2016-07-15' and value is zero. '2016-09-12' in hist is greater than '2016-09-01' in view, so it is not the previous
|  2 | 2016-08-01 |  5 | true | -- previous is '2016-05-05' and value is 3
|  3 | 2016-07-01 |  8 | false| -- there is no previous value in HIST table

我尝试了什么

我使用了以下查询。它适用于小负载,但在生产中性能失败,因为我的视图非常复杂且历史表太大。是否可以在不使用视图的情况下多次查询? (如果是这样,表现应该更好,我不会再看到超时)

您可以在这里测试http://rextester.com/l/sql_server_online_compiler

create table vw (id int, dt date, val int);
insert into vw values (1, '2016-09-01', 7), (2, '2016-08-01', 5), (3, '2016-07-01', 8);

create table hist (id int, dt date, val int);
insert into hist values (1, '2016-06-27', 4), (1, '2016-06-29', 3), (1, '2016-07-15', 0), (1, '2016-09-12', 8), (2, '2016-05-05', 3);

select vw.id, vw.dt, vw.val, (case when hist_with_flag.flag = 'true' then 'true' else 'false' end)
from vw
left join 
(
  select hist.id, (case when hist.val > 0 then 'true' else 'false' end) flag 
  from
  (
    select hist.id, max(hist.dt) as dt
    from hist
    inner join vw on vw.id = hist.id
    where hist.dt < vw.dt
    group by hist.id
  ) hist_with_max_dt
  inner join hist 
    on hist.id = hist_with_max_dt.id and hist.dt = hist_with_max_dt.dt
) hist_with_flag
on vw.id = hist_with_flag.id

3 个答案:

答案 0 :(得分:1)

您可以尝试使用此查询,它会返回与查询相同的结果。它应该在性能方面表现良好

SELECT vw.id, MAX(vw.dt) dt, 
    MAX(vw.val) val,  
    case when  MAX(h.val) > 0 then 'true' else 'false' END flag
FROM vw
OUTER APPLY(SELECT MAX(dt) dt FROM hist WHERE vw.id = hist.id 
                AND dt<vw.dt GROUP BY hist.id) t
LEFT JOIN hist h ON vw.id = h.id AND h.dt = t.dt
GROUP BY vw.id

答案 1 :(得分:1)

您可以使用带有“ROW_NUMBER”的简单JOIN来避免多个CTE

;with cte_1
as
(select vw.id, vw.dt, vw.val,hist.val HistVal,hist.dt HistDt,ROW_NUMBER()OVER (PARTITION BY vw.id,vw.dt ORDER BY vw.id,vw.dt,hist.dt desc) RNO
FROM vw
left join hist 
    on hist.id = vw.id and hist.dt < vw.dt
)
SELECT Id,Dt,Val,case when ISNULL(HistVal,0)=0 THEN 'FALSE' ELSE 'TRUE' END as FLAG
FROM cte_1 WHERE RNO=1

答案 2 :(得分:1)

您可以使用OUTER APPLY来获取之前的记录:

SELECT v.ID, v.DT, v.VAL,  
       IIF(t.VAL IS NULL OR t.VAL = 0, 'false', 'true') AS FLAG
FROM Vw AS v
OUTER APPLY (
   SELECT TOP 1 VAL, DT
   FROM Hist AS h 
   WHERE v.ID = h.ID AND v.DT > h.DT
   ORDER BY h.DT DESC) AS t