如何在给出之前获得N条记录?

时间:2015-07-10 14:15:46

标签: sql sql-server

如何在给出之前获得N条记录?

我有以下表结构:

Id, Message
1, John Doe
2, Jane Smith
3, Error
4, Jane Smith
5, Michael Pirs
7, Gabriel Angelos
8, Error

有没有办法在每个错误之前获取N条记录并加入所有这些记录? 因此,N = 2的预期结果将是

1, John Doe
2, Jane Smith
5, Michael Pirs
7, Gabriel Angelos

Fiddle

4 个答案:

答案 0 :(得分:3)

如果您的ID没有间隙增加,则需要创建一个行号列。然后你可以使用一个简单的连接来找到前面的N.你之前的N可能会重叠...所以如果你不想要重复,你必须添加distinct

declare @N as integer
set @N=2

;with cte_tbl (Id, Message, rownum) AS
(
select *, ROW_NUMBER() over (order by id) as rownum from test
)
select distinct Prev.Id, Prev.Message
from cte_tbl
join cte_tbl Prev
on Prev.rownum between cte_tbl.rownum-@N and cte_tbl.rownum-1
where cte_tbl.Message = 'Error'
    and Prev.Message <> 'Error'
order by Prev.Id

如果之前的@N条记录之一是错误,则“错误”记录将不会显示。如果您希望包含这些内容,则必须进行修改。只需删除行and Prev.Message <> 'Error'

即可

答案 1 :(得分:0)

您可以使用cross apply执行此操作。逻辑与典型应用程序略有不同,因为您只需要来自cross apply子查询的记录:

select t2.*
from table t cross apply
     (select top 2 t.*
      from table t2
      where t2.id < t.id
      order by t2.id desc
     ) t2
where t2.message = 'Error';

对于那些倾向的人,还有一种使用窗口函数的方法,但它有点麻烦。执行Error记录的反向累积和以识别给定错误之前的值。然后枚举这些并选择你想要的那些:

select t.id, t.message
from (select t.*, row_number() over (partition by grp order by id desc) as seqnum
      from (select t.*,
                   sum(case when message = 'Error' then 1 else 0 end) over
                       (order by id desc)) as grp
            from table t
           ) t
where seqnum between 2 and 3;

请注意,过滤器介于2和3之间,因为'Error'的值为1。

答案 2 :(得分:0)

获取所有'错误'的行并加入前面的id。假设您的ID是连续的。如果他们没有获得帮助或ROW_NUMBER()的连续ID。

你可以试试这个:

    select
    T.*
    from (
        select
            id iderror
        from myTable 
        where
            Message = 'Error'
    ) errorRows
    inner join myTable T on
        T.id between errorRows.iderror -2 and errorRows.iderror -1 and
        T.Message <> 'Error'

答案 3 :(得分:0)

如果您使用ID的标识字段,这会更容易一些,那么您将拥有连续的数字,但您可以使用此方法。我正在对行进行排名,然后在错误之前返回那些行。

select t1.Rank_ID, t1.id, t1.message, te.id
from (select rank() over(order by id) as Rank_ID, id, message from tbl_test) t1
inner join (select rank() over(order by id)as Rank_ID, id, message from tbl_test)  te
on t1.Rank_ID between te.Rank_ID-2 and te.Rank_ID-1
where te.message='Error'