Получить список клиентов, которые не заказывали определенный вид продукции

Это в SQL Server 2008 R2.

У нас есть 3 продукта, заказы и клиенты: P1 P2 P3

Мы хотели бы знать клиентов, которые заказали P1 и P2, НО НЕ P3. Мы не хотим получить клиентов, которые заказали все 3 вида продукции. Я не знаю, как написать этот запрос. Не могли бы вы посоветовать мне? Спасибо.

У нас всего 2 стола. Заказы и клиенты. Название продуктов фиксировано.


person TTCG    schedule 13.04.2012    source источник
comment
Вы хотите включить схему? Или нам угадывать?   -  person MatBailie    schedule 13.04.2012
comment
Да, пожалуйста, добавьте определения этих таблиц - в предварительно отформатированном разделе вопроса, а не в качестве комментария.   -  person halfer    schedule 13.04.2012
comment
может быть, если вы попробуете, вы будете знать, как это сделать в следующий раз ... покажите нам, что вы пробовали   -  person dansasu11    schedule 13.04.2012


Ответы (3)


Вынуждены угадать схему и предположить, что клиент может заказать один и тот же продукт несколько раз, вы можете использовать что-то вроде этого...

WITH
  Products (
    productID,
    inclusive
  )
AS
(
            SELECT 'P1', 1
  UNION ALL SELECT 'P2', 1
  UNION ALL SELECT 'P3', 0
)

SELECT
  customerID
FROM
  Orders
INNER JOIN
  Products
    ON Orders.ProductID = Products.ProductID
GROUP BY
  customerID
HAVING
  COUNT(distinct Orders.ProductID) = (SELECT SUM(inclusive) FROM Products)
  AND MIN(Products.inclusive)      = 1

Во-первых, соединение отфильтровывает все, что принадлежит только Заказам, включая ЛЮБОЙ из P1, P2 или P3.

GROUP BY объединяет их в группы заказов, по одной группе для каждого клиента.

Первое предложение HAVING просматривает все заказы в этом списке. Он подсчитывает количество разных товаров в этом подмножестве. Он проверяет количество инклюзивных продуктов в списке продуктов, который мы ищем. Он предусматривает, что эти две цифры должны быть одинаковыми.
[В этом примере; они должны были заказать ровно два разных продукта.]

Второе предложение HAVING проверяет, имеет ли какой-либо из этих продуктов inclusive = 0.
[Ни один продукт, заказанный покупателем, не может отображаться в списке исключений.]


EDIT: Это альтернатива, которую некоторые люди предпочитают, но я думаю, что она менее эффективна (в случаях, когда таблица Orders имеет значительный размер).

SELECT
  customerID
FROM
  Orders
WHERE
  ProductID in ('P1', 'P2')
GROUP BY
  customerID
HAVING
  COUNT(distinct ProductID) = 2
  AND NOT EXISTS (SELECT *
                    FROM Orders AS lookup
                   WHERE CustomerID = Orders.CustomerID
                     AND ProductID IN ('P3')
                 )
person MatBailie    schedule 13.04.2012
comment
Я думаю, вы имели в виду COUNT(distinct Orders.ProductID) >= (SELECT SUM(inclusive) FROM Products), то есть >= вместо =, потому что, если клиент заказал P1, P2, P4, он все равно должен быть включен в вывод (или не должен?). - person Andriy M; 16.04.2012
comment
@AndriyM - Нет; должен был быть INNER JOIN. 'P4' затем исключается из COUNT(). Должно быть, спал, когда писал LEFT :) - person MatBailie; 16.04.2012
comment
Ты прав. По моему мнению, клиент с P1, P4, P5 тоже будет (неправильно) включен. - person Andriy M; 16.04.2012

предполагая, что Orders содержит столбцы CustomerID и ProductId

select disrinct(customerID) from Orders

дает вам всех клиентов, которые разместили заказы

select customerID, COUNT(distinct(productID))
from Orders
where productID in ('P1', 'P2') 
      and customerid not in (select customerID
from Orders
where productID in ('P3'))
group by customerID
having COUNT(distinct(productID))>1

дает вам всех клиентов, которые разместили заказы с продуктом P1 и P2, но не P3

person Diego    schedule 13.04.2012
comment
where productID in ('P1', 'P2') and productID not in ('P3') это не сработает (кстати, это не мой голос против). это покажет только клиентов, заказавших p1 ИЛИ p2, без учета p3. когда productID строки = 'p1', это не будет 'p3', поэтому он будет включен в набор результатов, даже если для этого клиента существует другая строка p3. Это даже вернет клиентов, которые заказали только p1, а не p2. Помните, WHERE просматривает значения каждой строки независимо. - person KM.; 13.04.2012
comment
боже ты прав! Я пропустил это. Просто предоставил другое решение, которое может быть полезным - person Diego; 13.04.2012
comment
@Diego - это возвращает клиента, даже если он заказал только один из P1 или P2, но OP требует, чтобы клиент возвращался только, если он заказал < я>оба. - person MatBailie; 13.04.2012
comment
извините ребята, это пятница 5 часов. Я снова отредактировал. Решение не очень хорошее, но структура таблицы не очень. - person Diego; 13.04.2012

person    schedule
comment
Трудно масштабировать до большего количества элементов в списке. Итак, хорошее конкретное решение, не очень хорошее общее решение - person MatBailie; 13.04.2012