SQL — группа при возникновении в x или y

#sql #postgresql

#sql #postgresql

Вопрос:

Мне трудно заставить работать следующее:

У меня есть список транзакций, состоящий из Sender , Recipient , Amount и Date .

 Table: Transactions

Sender   Recipient   Amount    Date
--------------------------------------------------
Jack     Bob         52        2019-04-21 11:06:32
Bob      Jack        12        2019-03-29 12:08:11
Bob      Jill        50        2019-04-19 24:50:26
Jill     Bob         90        2019-03-20 16:34:35
Jill     Jack        81        2019-03-25 12:26:54
Bob      Jenny       53        2019-04-20 09:07:02
Jack     Jenny       5         2019-03-29 06:15:35
  

Теперь я хочу перечислить людей, которые участвовали в транзакциях, в скольких транзакциях они участвовали, и даты первой и последней транзакции, в которой они участвовали :

 Result

Person   NUM_TX   First_active               last_active    
------------------------------------------------------------------
Jack     4        2019-03-25 12:26:54        2019-04-21 11:06:32
Bob      5        xxxx-xx-xx xx:xx:xx        xxxx-xx-xx xx:xx:xx
Jill     3        xxxx-xx-xx xx:xx:xx        xxxx-xx-xx xx:xx:xx
Jenny    2        xxxx-xx-xx xx:xx:xx        xxxx-xx-xx xx:xx:xx
  

Использование group by оператора кажется неправильным — каков правильный способ достижения моей цели? Кстати, я работаю на postgres.

Ответ №1:

Вам нужно ОБЪЕДИНЕНИЕ, чтобы получить 2 столбца как 1 столбец person результирующего набора, а затем group by person :

 select 
  t.person Person,
  count(*) NUM_TX,
  min(t.date) First_active,
  max(t.date) Last_active
from (
  select sender person, date from transactions
  union all
  select recipient person, date from transactions
) t
group by t.person
  

Комментарии:

1. Этот ответ также работает — поскольку я обрабатываю очень огромные объемы данных, другой помечен как принятый

Ответ №2:

Это хорошее место для использования бокового соединения:

 select v.person, count(*) as num_transactions,
       min(t.date) as first_date,
       max(t.date) as last_date
from transactions t cross join lateral
     (values (sender), (recipient)) v(person)
group by v.person;
  

Комментарии:

1. Круто, никогда не слышал об этом! Является ли боковое объединение предпочтительнее объединения для большого объема данных или я должен придерживаться решения ОБЪЕДИНЕНИЯ?

2. @Crack_David . , , Боковое соединение, в частности, лучше работает с большими объемами данных, потому что оно сканирует таблицу только один раз.

3. Привет @GordonLinoff Мне удалось заменить боковое соединение CROSS APPLY на SQL Server и получить тот же результат. Однако я не уверен, что именно (values (sender), (recipient)) v(person) делает. Я пытался SELECT * FROM (values (sender), (recipient)) v(person) , но это не удалось. Я думаю, вы берете отправителей и получателей и помещаете их в один столбец (как и в другом ответе, но намного быстрее). Но я учусь CROSS APPLY , поэтому я немного смущен. Здесь скрипка SQL; dbfiddle.uk /…

4. @NicholasHumphrey . , , Если у вас есть вопрос, его следует задавать как вопрос, а не комментарий.

5. @GordonLinoff Конечно, извините за это. Позвольте мне перефразировать это в вопрос.