Как найти минимум и максимум каждой группы с SQL? - PullRequest
1 голос
/ 16 июня 2019

Я пытаюсь понять, как я могу сделать это в SQL. У меня есть таблица со следующими столбцами в таблице Customer - (Customer_Id, Gender, Birthday). Вопрос в том, что мне нужно найти самого раннего и последнего родившегося по полу. По сути Мин и Макс по разным группам.

123 M   2017-07-05 00:00:00.000
345 M   2016-08-01 00:00:00.000
555 F   2012-01-09 00:00:00.000
567 F   2015-02-07 00:00:00.000
789 F   2013-01-02 00:00:00.000
111 F   2000-01-01 00:00:00.000
188 M   2008-09-01 00:00:00.000

Результирующий набор должен выглядеть следующим образом

188     M       2008-09-01 00:00:00.000
123     M       2017-07-05 00:00:00.000
111     F       2000-01-01 00:00:00.000
567     F       2015-02-07 00:00:00.000

Я мог бы сделать 4 UNION S и понять это таким образом, но это было бы неэффективно.

Вот то, что я придумал, но это тоже не сработает. Как мне сделать это для групп MAX также в одном запросе?

    select a.Customer_id, a.gender, b.min_birthday
    from(
    select gender, min(birthday) min_birthday
    from Sales..Customer group by gender) b join Sales..Customer a on b.gender = a.gender 
    and b.min_birthday = a.birthday

Ответы [ 3 ]

1 голос
/ 16 июня 2019

Один метод использует оконные функции:

select customer_id, gender, birthday
from (select c.*,
             row_number() over (partition by gender order by birthday) as seqnum_asc,
             row_number() over (partition by gender order by birthday desc) as seqnum_desc
      from customer c
     ) c
where 1 in (seqnum_asc, seqnum_desc);

Используйте rank() вместо row_number(), если хотите связи.

Тем не менее, с индексом (gender, birthday) и (gender, birthday desc) (оба индекса могут больше не понадобиться, если оптимизатор улучшился), подход union all должен работать очень хорошо:

select c.*
from ((select top (1) c.*
       from customer c
       where gender = 'M'
       order by birthday
      ) union all
      (select top (1) c.*
       from customer c
       where gender = 'F'
       order by birthday
      ) union all
      (select top (1) c.*
       from customer c
       where gender = 'M'
       order by birthday desc
      ) union all
      (select top (1) c.*
       from customer c
       where gender = 'F'
       order by birthday desc
      )
     ) c;
0 голосов
/ 16 июня 2019

Вы можете сделать это с NOT EXISTS:

select c.* from Customer c
where not exists (
  select 1 from Customer
  where gender = c.gender and birthday < c.birthday
) or not exists (
  select 1 from Customer
  where gender = c.gender and birthday > c.birthday
)
order by c.gender, c.birthday

См. Демоверсию .Результаты:

> id  | gender | birthday           
> :-- | :----- | :------------------
> 111 | F      | 01/01/2000 00:00:00
> 567 | F      | 07/02/2015 00:00:00
> 188 | M      | 01/09/2008 00:00:00
> 123 | M      | 05/07/2017 00:00:00
0 голосов
/ 16 июня 2019

На самом деле достаточно одного UNION ALL :)

select gender, min(birthday) birthday, 'MIN' Aggregate
from Sales..Customer group by gender
union all
select gender, max(birthday), 'MAX'
from Sales..Customer group by gender

Но лучшая производительность была бы с:

select gender, min(birthday), max(birthday)
from Sales..Customer group by gender

Но результат будет немного отличаться от желаемого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...