Выбор только верхней строки родительской таблицы со всеми дочерними строками таблицы

#sql #sql-server

#sql #sql-сервер

Вопрос:

Итак, у меня есть две таблицы:

 #ProjectHealthReports
Id | From       | SubmittedOn
1  | 2020-01-01 |
2  | 2020-02-01 | 2020-10-23
3  | 2020-03-01 |
4  | 2020-04-01 | 2020-10-23
5  | 2020-05-01 | 2020-10-23


#ProjectHealthReportItems
Id | Note                                      | ProjectHealthReportId
1  | First for 2020-01-01                      | 1
2  | Second for 2020-01-01                     | 1
3  | First for 2020-02-01                      | 2
4  | Second for 2020-02-01                     | 2
5  | First for 2020-03-01                      | 3
6  | Second for 2020-03-01                     | 3
7  | First for 2020-04-01                      | 4
8  | Second for 2020-04-01                     | 4
9  | (We want this one) First for 2020-05-01   | 5
10 | (We want this one) Second for 2020-05-01  | 5
  

Как я могу получить все #ProjectHealthReportItems и #ProjectHealthReport подробности для последней From даты, которая имеет значение для SubmittedOn (так что в данном случае это будут ProjectHealthReport 5 и ProjectHealthReportItems 9, 10).

В принципе, мне нужно что-то вроде этого, очевидно, без top 1 , поскольку он возвращает только одну строку, и в этом случае мне нужно вернуть 2 строки 🙂

 select top 1 phr.Id, phr.[From], phr.SubmittedOn, phri.Note from #ProjectHealthReports phr
inner join #ProjectHealthReportItems phri on phr.Id = phri.ProjectHealthReportId
where phr.SubmittedOn is not null
order by phr.[From] desc
  

Вот SQL для создания и заполнения таблиц

 create table #ProjectHealthReports(
    Id int primary key,
    [From] date not null ,
    SubmittedOn date null
)
go
create table #ProjectHealthReportItems(
    Id int primary key,
    Note nvarchar(max),
    ProjectHealthReportId int constraint FK_PHR references #ProjectHealthReports
)
go

insert into #ProjectHealthReports(Id, [From], SubmittedOn)
values (1, '2020-01-01', null),
       (2, '2020-02-01', getutcdate()),
       (3, '2020-03-01', null),
       (4, '2020-04-01', getutcdate()),
       (5, '2020-05-01', getutcdate())
go

insert into #ProjectHealthReportItems(Id, Note, ProjectHealthReportId)
values (1, 'First for 2020-01-01', 1),
       (2, 'Second for 2020-01-01', 1),
       (3, 'First for 2020-02-01', 2),
       (4, 'Second for 2020-02-01', 2),
       (5, 'First for 2020-03-01', 3),
       (6, 'Second for 2020-03-01', 3),
       (7, 'First for 2020-04-01', 4),
       (8, 'Second for 2020-04-01', 4),
       (9, '(We want this one) First for 2020-05-01', 5),
       (10, '(We want this one) Second for 2020-05-01', 5)
go
  

Ответ №1:

Сначала выберите top затем join

    select t.*, phri.Note
   from (select top(1) phr.Id phrid, phr.[From], phr.SubmittedOn
         from #ProjectHealthReports phr
         where phr.SubmittedOn is not null
         order by phr.[From] desc) t
   inner join #ProjectHealthReportItems phri on t.phrId =   phri.ProjectHealthReportId
  

Ответ №2:

Я бы предложил оконные функции:

 select phr.*, phri.*
from #ProjectHealthReports phr left join
     (select phri.*,
             row_number() over (partition by ProjectHealthReportId order by id desc) as seqnum
      from #ProjectHealthReportItems phri
     ) phri
     on phr.Id = phri.ProjectHealthReportId and seqnum = 1
order by phr.[From] desc;
  

Вы также можете сделать это, используя фильтрацию в where , например, коррелированный подзапрос:

 select phr.*, phri.*
from #ProjectHealthReports phr join
     #ProjectHealthReportItems phri
     on phr.Id = phri.ProjectHealthReportId and seqnum = 1
where phri.id = (select max(phri2.id)
                 from #ProjectHealthReportItems phri2
                 where phri2.ProjectHealthReportId = phri.ProjectHealthReportId
                )
order by phr.[From] desc
  

Ответ №3:

Эффективным способом сделать это без объединения по ЛЕВОМУ КРАЮ было бы присвоить номер строки, используя ROW_NUMBER() оконную функцию, таблице #projectthealthreports . Что-то вроде этого

 with lv_cte as (
    select *, row_number() over (order by [From] desc) rn
    from #ProjectHealthReports)
select l.*, phri.* 
from lv_cte l
     join #ProjectHealthReportItems phri on l.id=phri.ProjectHealthReportId
where l.rn=1;

  

Вывод

 Id  From        SubmittedOn rn  Id  Note                              ProjectHealthReportId
5   2020-05-01  2020-10-23  1   9   (We want this one) First for 2020-05-01 5
5   2020-05-01  2020-10-23  1   10  (We want this one) Second for 2020-05-01    5