Выберите клиентов, которые заказали только c конференций и ничего больше - PullRequest
1 голос
/ 03 августа 2020

Представьте, что у нас есть таблица клиентов и таблица заказов, которые связаны между собой следующим образом: client.id = order.client_id. Запись в таблице заказов содержит идентификатор приобретенного продукта. Запись в таблице продуктов содержит идентификатор конференции, на которой этот продукт был приобретен. Как мне выбрать клиентов, которые приобрели только продукты из списка конкретных c конференций?

Я мог бы решить эту проблему с помощью сценария PHP, сделав несколько запросов, переупорядочив данные, но это не весело *. 1003 *

Я пробовал это до сих пор:

select
    o.client_id,
    array_agg(p.conference_id) as conference_ids
from product as p
left join order as o
    on o.id = p.order_id
where
    p.conference_id = any('{ 46545, 46543 }'::int[])

Но это не сработало, так как выбираются клиенты, которые приобрели не только на этих, но и на других конференциях.

Изменить: исправлено sql, чтобы быть синтаксически правильным

Ответы [ 3 ]

1 голос
/ 03 августа 2020

Один из способов приблизиться к этому без использования массивов:

select client.id
from product as p
left join order as o on o.id = p.order_id
group by client.id
having count(*) filter(where p.conference_id not in (46545, 46543)) = 0;

Если вы также хотите утверждать, что обе из двух вышеупомянутых конференций также были посещены (т. Е. Присутствовали на обеих, но не другие), вы можете добавить еще одно утверждение в предложение HAVING:

select client.id
from product as p
left join order as o on o.id = p.order_id
group by client.id
having count(*) filter(where p.conference_id not in (46545, 46543)) = 0 and
       min(p.conference_id) <> max(p.conference_id);
0 голосов
/ 03 августа 2020

Предположительно у вас есть таблица клиентов. Один забавный метод использует операцию set:

select c.id
from clients c
except
select o.client_id
from orders o join
     products p
     on o.id = p.order_id
where p.conference_id not in (46545, 46543);

На самом деле, я предпочитаю методы агрегирования - они более универсальны. Но запрос в вашем вопросе синтаксически неверен, и я чувствую, что вы хотели бы использовать таблицу clients.

0 голосов
/ 03 августа 2020

Вы можете использовать агрегирование и фильтр с предложением having.

Ваш запрос ссылается на таблицу client, которую вы не выбираете - это синтаксическая ошибка. Вместо этого вы можете получить client_id прямо из таблицы заказов.

select o.client_id, array_agg(p.conference_id) as conference_ids
from orders as o 
inner join product as p on p.product_id = o.id
group by o.client_id
having count(*) filter(where not p.conference_id = any('{ 46545, 46543 }'::int[])) = 0

Обратите внимание, что order - ключевое слово языка, поэтому это плохой выбор для имени таблицы; Вместо этого я переименовал его в orders.

Если вы хотите, чтобы клиент размещал заказы на все конференции, перечисленные в массиве, вы можете добавить еще одно условие в предложение having; вы можете сделать это независимо от количества элементов массивов с помощью array_length():

having 
    count(distinct p.conference_id) 
        filter(where p.conference_id = any('{ 46545, 46543 }'::int[])) = array_length('{ 46545, 46543 }'::int[], 1)
    and count(*) filter(where not p.conference_id = any('{ 46545, 46543 }'::int[])) = 0
...