SQL-选择倒数第二个和倒数第二个

时间:2020-06-10 02:52:26

标签: sql-server-2016

问题:简化的成员资格表,其中包含成员资格ID,每个成员资格的开始日期和成员资格级别描述:

CREATE TABLE cover
(
    [membership_id] int, 
    [cover_from_date] date, 
    [description] varchar(57)
);

INSERT INTO cover ([membership_id], [cover_from_date], [description])
VALUES (1, '1/1/2011', 'AA'),
       (1, '1/2/2011', 'BB'),
       (1, '1/3/2011', 'CC'),
       (1, '1/4/2011', 'CC');

任务:列出当前成员资格以及与当前成员不同的直接前一个成员资格。因此,从上表中我想看到类似的东西:

1, 1/4/2011, CC, 1/2/2011, BB

已尝试的解决方案:我设法提出了一个解决方案,但是在大型数据库上运行需要花费大量时间,而且我敢肯定有解决此问题的更好方法。我对复杂查询的无疑是:

with cte as 
(
    select
        cover.membership_id, cover.cover_from_date, 
        cover.description, 
        row_number() over (partition by cover.membership_id order by  cover.cover_from_date desc) AS version_no
    from
        cover
)
select 
    cte.membership_id, 
    cover_now.cover_from_date, cover_now.description, 
    cover_prev.cover_from_date, cover_prev.description
from
    cte
left outer join 
    cte cover_now on cte.membership_id = cover_now.membership_id
                  and cover_now.version_no = 1
left outer join 
    cte cover_prev on cte.membership_id = cover_prev.membership_id
                   and cover_prev.version_no = (select min(x.version_no)
                                                from  cte x
                                                where x.version_no >= 2
                                                  and x.membership_id = cover_now.membership_id
                                                  and x.description <> cover_now.description)
group by 
    cte.membership_id, cover_now.cover_from_date, cover_now.description, 
    cover_prev.cover_from_date, cover_prev.description

整个小提琴位于here。有关如何优化查询的任何提示将不胜感激。

1 个答案:

答案 0 :(得分:1)

首先按降序在Membership_id和cover_from_date上创建索引。该查询将大量使用它。

create index cover_by_date on cover (membership_id asc, cover_from_date desc)

然后:

select
   membership.membership_id,
   membership.cover_from_date,
   membership.description,
   previous_membership.cover_from_date,
   previous_membership.description
from 
   (
      select membership_id, description, cover_from_date, row_number() over (partition by membership_id order by cover_from_date desc) as rank
      from cover
   ) as membership
   left join (
      select previous.membership_id, previous.description, previous.cover_from_date, row_number() over (partition by previous.membership_id order by previous.cover_from_date desc) as rank
      from cover
         join cover as previous on
            cover.membership_id = previous.membership_id and
            cover.description <> previous.description and
            cover.cover_from_date > previous.cover_from_date
   ) as previous_membership on
      previous_membership.membership_id = membership.membership_id and
      previous_membership.rank = 1
where
   membership.rank = 1