#sqlite #group-by #count
#sqlite #группировать по #количество
Вопрос:
У меня есть таблица SQLite, в которой я хотел бы отфильтровать клиентов, которые купили 2 или более ОТДЕЛЬНЫХ товара. Таблица выглядит следующим образом:
customer | product | quantity | date of purchase
---------------------------------------------
John | coffee | 1 | 02.20.2019
Bob | coffee | 2 | 01.13.2018
Chris | milk | 1 | 02.03.2012
Alice | bread | 1 | 05.01.2019
John | banana | 2 | 08.04.2015
Mary | orange | 3 | 12.16.2018
Mary | orange | 2 | 04.21.2019
Chris | pie | 1 | 07.02.2004
Мне удалось выбрать клиентов, сгруппировав их с помощью двух строк в ГРУППЕ ПО запросу (клиент и продукт):
SELECT * FROM test_table GROUP BY customer, product
Это (почти) дало мне всех клиентов, которых я изначально хотел, за исключением того, что в него также вошла Мэри, что не является желаемым результатом (она не покупала два разных продукта). Я попытался использовать функцию COUNT DISTINCT, чтобы показывать только клиентов, которые купили два или более разных продукта, например:
SELECT customer, product, COUNT(DISTINCT(product)) AS counter FROM test_table
GROUP BY customer, product HAVING counter>1
Запрос успешно вернул Chris и John без Mary, однако он выводит их только один раз, показывая только один из продуктов, которые они купили. Есть ли способ, которым я мог бы предоставить Крису и Джону все отдельные продукты, которые они купили? Ожидаемый результат запроса:
John | coffee | 1 | 02.20.2019
John | banana | 2 | 08.04.2015
Chris | milk | 1 | 02.03.2012
Chris | pie | 1 | 07.02.2004
Ответ №1:
С помощью агрегации:
SELECT *
FROM test_table
WHERE customer IN (SELECT customer
FROM test_table
GROUP BY customer
HAVING MIN(product) <> MAX(product));
В HAVING
приведенном выше предложении утверждается, что «минимальные» и «максимальные» продукты не совпадают, что подразумевает, что с каждым соответствующим клиентом связано как минимум два разных продукта.
Для более сложного решения мы могли бы использовать аналитические функции здесь:
WITH cte AS (
SELECT *, MIN(product) OVER (PARTITION BY customer) AS min_product,
MAX(product) OVER (PARTITION BY customer) AS max_product
FROM test_table
)
SELECT customer, product, quantity, date_of_purchase
FROM cte
WHERE min_product <> max_product;
Комментарии:
1. Это не дает желаемого результата. Он возвращает каждого клиента только один раз.
2. @Beartech Извините, в спешке я не удосужился проверить ваш точный ожидаемый результат. Пожалуйста, рассмотрите мой обновленный ответ.
Ответ №2:
Вы можете использовать EXISTS
для получения всех строк:
SELECT t.*
FROM test_table t
WHERE EXISTS (SELECT 1 FROM test_table WHERE customer = t.customer AND product <> t.product)
Результат:
> customer | product | quantity | date_of_purchase
> :------- | :------ | -------: | :---------------
> John | coffee | 1 | 02.20.2019
> Chris | milk | 1 | 02.03.2012
> John | banana | 2 | 08.04.2015
> Chris | pie | 1 | 07.02.2004
Или, если вам нужна 1 строка для каждого customer
с продуктами и датами в виде списков, разделенных запятыми, вы можете использовать GROUP_CONCAT()
:
SELECT customer,
GROUP_CONCAT(product) products,
GROUP_CONCAT(date_of_purchase) dates
FROM test_table
GROUP BY customer
HAVING COUNT(DISTINCT product) > 1
Результат:
> customer | products | dates
> :------- | :------------ | :--------------------
> Chris | milk,pie | 02.03.2012,07.02.2004
> John | coffee,banana | 02.20.2019,08.04.2015
Посмотрите демонстрацию.