2016-11-14 2 views
0

Предположим, у меня есть эти три таблицы:SQL стратегия для извлечения максимальной

enter image description here

Я хочу, чтобы получить, для всех продуктов, это PRODUCT_ID и клиент, который bougth это самый раз (самый большой клиент продукта).

Я решил так:

SELECT 
product_id AS product, 
(SELECT TOP 1 client_id FROM Bill_Item, Bill 
    WHERE Bill_Item.product_id = p.product_id 
    and Bill_Item.bill_id = Bill.bill_id 
GROUP BY 
    client_id 
ORDER BY 
    COUNT(*) DESC 
) AS client 
FROM Product p 

Вы знаете лучший способ?

+0

Какую версию SQL вы используете? –

+0

SQL Server 2012 –

+1

Не уверен, будет ли он оптимизировать запрос, но рекомендуется использовать 'JOIN', поэтому ваш запрос может быть изменен на' FROM Bill_Item INNER JOIN Bill ON Bill_Item.product_id = p.product_id и Bill_Item.bill_id = Bill.bill_id' Вы можете использовать 'SHOWPLAN' для проверки плана выполнения. –

ответ

1

Я собирался представить в значительной степени то же самое, что @Squirrell только с общим выражением таблицы [CTE], а не с производной таблицей. Поэтому я не буду дублировать это, но есть некоторые точки обучения по вашему запросу. Первый - IMPLICIT JOINS, такие как FROM Bill_Item, Bill, действительно легко иметь ожидаемые последствия (один из многих вопросов: Queries that implicit SQL joins can't do?). Далее для вычисленного столбца вы можете сделать это в OUTER APPLY или CROSS APPLY, что является очень полезной техникой.

Таким образом, вы могли бы заново написать свой метод следующим образом:

SELECT * 
FROM 
    Product p 
    OUTER APPLY (SELECT TOP 1 b.client_id 
      FROM 
       Bill_Item bi 
       INNER JOIN Bill b 
       ON bi.bill_id = b.bill_id 
      WHERE 
       bi.product_id = p.product_id 
      GROUP BY 
       b.client_id 
      ORDER BY 
       COUNT(*) DESC) c 

И чтобы показать вам, как ответ Squirell может еще включать в себя продукты, которые никогда не были проданы все, что вам нужно сделать, это присоединиться к продукции и LEFT JOIN к другие таблицы:

;WITH cte AS (
    SELECT 
     p.product_id 
     ,b.client_id 
     ,ROW_NUMBER() OVER (PARTITION BY p.product_id ORDER BY COUNT(*) DESC) as RowNumber 
    FROM 
     Product p 
     LEFT JOIN Bill_Item bi 
     ON p.product_id = bi.product_id 
     LEFT JOIN Bill b 
     ON bi.bill_id = b.bill_id 
    GROUP BY 
     p.product_id 
     ,b.client_id 
) 

SELECT * 
FROM 
    cte 
WHERE 
    RowNumber = 1 

Методы, используемые в некоторых из них, которые являются полезными.

  • КТР
  • ОТНОСИТЬСЯ (Outer & Cross)
  • Функции окна
2

внутренний запрос предоставит вам рейтинг. Внешний запрос даст вам клиент, который puchase наиболее продукта

SELECT * 
(
    SELECT i.product_id, b.client_id, 
      r = row_number() over (partition by i.product_id 
             order by count(*) desc) 
    FROM Bill b 
      INNER JOIN Bill_Item i ON b.bill_id = i.bill_id 
    GROUP BY i.product_id, b.client_id 
) d 
WHERE r = 1 
1

ответ Белки не возвращает продукты, которые никогда не были проданы. Если вы хотите включить те, то ваш подход хорошо, хотя я хотел бы написать запрос как:

SELECT product_id as product, 
     (SELECT TOP 1 b.client_id 
     FROM Bill_Item bi JOIN 
      Bill b 
      ON bi.bill_id = b.bill_id 
     WHERE Bill_Item.product_id = p.product_id 
     GROUP BY client_id 
     ORDER BY COUNT(*) DESC 
     ) as client 
FROM Product p; 

Вы также можете выразить это с помощью APPLY, а коррелированные подзапрос тоже хорошо.

Обратите внимание на правильное использование явного синтаксиса JOIN.

Смежные вопросы