Я новичок в написании запросов, поэтому не уверен, возможно ли вообще то, что я пытаюсь сделать. Я хочу написать запрос, который даст мне список клиентов, у которых есть только сберегательный счет и никаких других продуктов.
Вот как сохраняются данные:
Я хочу, чтобы результат показывал ТОЛЬКО Сару и Шелли.
но лучшее, что я могу придумать, — это Джейн, Сара и Шелли. Как мне отфильтровать результаты Джейн?
SELECT A.customer_name
FROM A.Table1
INNER JOIN B.Table2
ON A.acct_number = B.acct_number
WHERE B.account_type = 'savings';
А как насчет клиента, у которого есть 2 сберегательных счета (и больше ничего)?
Также добавьте тег для используемой базы данных. Как вы видите ниже, разные продукты имеют несколько разную функциональность.
Вы можете сгруппировать по имени, чтобы получить количество учетных записей каждого, а затем отфильтровать это по людям, у которых только одна учетная запись, используя наличие (это похоже на where
, но фильтруется после группировки).
Затем мы также можем проверить, какой тип учетной записи у них есть. Обычно вы не можете сделать это с помощью group by
, потому что каждая группа будет иметь несколько строк разных типов. Но поскольку мы ищем людей всего с одной строкой, мы можем использовать max(type), поскольку в каждой группе только один тип, он всегда будет возвращать только type
.
(Большинство баз данных также имеют способ объединить все значения в группу, и это может быть лучшим выбором, чем max
, но они нестандартны.)
select name
from customers c
left join accounts a on c.account_num = a.account_num
group by name
having count(*) = 1 and max(type) = 'savings'
Демонстрация.
Или вы можете использовать предложение совокупного фильтра, чтобы учитывать только их несберегательные счета. Большинство баз данных поддерживают его, но не MySQL.
select name
from customers c
left join accounts a on c.account_num = a.account_num
group by name
having count(*) filter(where type <> 'savings') = 0
А для более общего случая (например, проверка «кредита», который не является ни максимальным, ни минимальным значением символа) вы можете использовать что-то вроде max(CASE WHEN type='loan' THEN 1 ELSE 0 END) = 1
@JonasMetzler Вероятно, лучше, если база данных поддерживает это. MySQL нет.
@JonasMetzler Я не знаю, какую базу данных они используют, поэтому я выбрал самое простое и совместимое. Вы можете написать свой собственный ответ, если хотите охватить больше вариантов.
Я бы использовал условную агрегацию в предложении HAVING
и просто подсчитывал все вхождения с экономией типа <>. Если ни один не появится, этот клиент будет выбран.
«Стандартный» способ поддержки всех СУБД.
HAVING COUNT(CASE WHEN type <> 'savings' THEN 1 END) = 0;
Все больше и больше СУБД предоставляют предложение FILTER
, они более читабельны:
HAVING COUNT(*) FILTER(WHERE type <> 'savings') = 0;
MySQL предоставляет самый короткий синтаксис, там можно написать SUM(boolean expression)
:
HAVING SUM(type <> 'savings') = 0;
Таким образом, весь запрос будет:
SELECT
c.name
FROM
customers c
LEFT JOIN accounts a --or INNER JOIN, check what you need
ON c.account_num = a.account_num
GROUP BY
c.name
HAVING
COUNT(CASE WHEN a.type <> 'savings' THEN 1 END) = 0;
--replace by FILTER clause if your RDBMS supports it
Вот демо-версия Postgres, которая поддерживает как опцию FILTER
, так и опцию CASE
.
Другой подход, используйте EXCEPT
:
SELECT name FROM customers WHERE type = 'savings'
EXCEPT
SELECT name FROM customers WHERE type <> 'savings'
Однако вернет клиента, имеющего 2 сберегательных счета (и ничего больше).
Добро пожаловать в Stack Overflow. Благодарим вас за предоставление примеров данных, однако из-за скриншотов сложнее отвечать на вопросы. Пожалуйста, предоставьте образец данных в виде текста. Вы можете использовать форматирование таблицы, которое можно найти на вкладке «Справка» редактора. А еще лучше создать образец базы данных на dbfiddle, чтобы другие могли его использовать.