Как посчитать количество объектов в таблице, сгруппированных по столбцу в has_many, для которых имеют значение некоторые условия? - PullRequest
2 голосов
/ 20 сентября 2019

У меня есть таблица с именем cars (id, brand).У автомобилей может быть много adverts (id, car_id, state, dealer_name).

Я хочу посчитать количество уникальных автомобилей в группах по dealer_name, для которых данный конкретный дилер является ТОЛЬКО тем, у которого в данный момент есть автомобиль for sale

https://www.db -fiddle.com / f / cjstvXSZ2sWYNqfe6LRcTc / 1 (там также записан ожидаемый результат запроса)

Мне удалосьделать это итеративно в приложении ruby, выполняя один запрос для каждого dealer_name, однако мне не удается сделать это в одном запросе на группировку.

Данные:

cars
id    make
1     'BMW'
2     'Mercedes'
3     'Mercedes'
4     'Volvo'
5     'Volvo'
6     'Volvo'
7     'Alfa Romeo'

adverts
id    car_id  state       dealer_name
1     1       'sold'      'Dealer 1'
2     2       'for sale'  'Dealer 1'
4     3       'for sale'  'Dealer 2'
5     4       'for sale'  'Dealer 2'
6     5       'for sale'  'Dealer 3'
7     5       'for sale'  'Dealer 4'
8     7       'sold'      'Dealer 4'

expected output:
dealer_name   Number of cars with only this dealer having the car currently 'for sale'
Dealer 1      1
Dealer 2      2
Dealer 3      0
Dealer 4      0

Ответы [ 2 ]

4 голосов
/ 20 сентября 2019

Вы можете выполнить подсчет окон в подзапросе, а затем использовать условное агрегирование во внешнем запросе:

select 
    dealer_name, 
    sum(case when cnt = 1 and state = 'for sale' then 1 else 0 end) cnt
from (
    select 
        dealer_name, 
        state,
        car_id, 
        count(*) over(partition by car_id, state) cnt
    from adverts a
) x 
group by dealer_name
order by dealer_name;

Демонстрация на БД Fiddle :

| dealer_name | cnt |
| ----------- | --- |
| Dealer 1    | 1   |
| Dealer 2    | 2   |
| Dealer 3    | 0   |
| Dealer 4    | 0   |

Примечание: это был хорошо задокументированный вопрос, что SO может использовать больше!

1 голос
/ 20 сентября 2019

Если вы можете иметь дубликаты автомобилей для одной и той же сделки (что подразумевается вашим желанием считать разные автомобили), тогда это сложнее.Postgres не поддерживает count(distinct) в качестве функции агрегирования.

Один из методов:

select dealer_name,
       count(distinct car_id) filter (where state = 'for sale' and min_dn = max_dn) as cnt
from (select a.*,
             min(a.dealer_name) over (partition by a.car_id, a.state) as min_dn,
             max(a.dealer_name) over (partition by a.car_id, a.state) as max_dn
      from adverts a
     ) a 
group by dealer_name
order by dealer_name;

Или вы можете использовать два уровня агрегации:

select dealer_name,
       count(*) filter (where for_sale and num_dealers = 1) as cnt
from (select dealer_name, car_id,
             bool_or(state = 'for sale') as for_sale,
             count(*) filter (where state = 'for sale' over (partition by car_id)) as num_dealers
      from adverts
      group by dealer_name, car_id
     ) dc
group by dealer_name;

Этитакже верните всех дилеров, даже если у них нет автомобилей для продажи.

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