Повторное внешнее соединение SQL, повышающее эффективность - PullRequest
0 голосов
/ 18 сентября 2018

Заявление Probelm:

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

Продажи определяются с использованием Sales.Orders и Sales.OrderLines, как и в предыдущих двух вопросах. Однако, поскольку мы запрашиваем эту информацию с точки зрения поставщика, вам также необходимо использовать таблицы Warehouse.StockItems и Purchasing.Suppliers.

Необходимые столбцы в наборе результатов:

SupplierID - Как указано в таблице Закупки. Поставщики.
SupplierName - Как указано в таблице Закупки. Поставщики.
OrderCount - Количество заказов, выставленных на товары для каждого поставщика.
Продажи - промежуточный итог из размещенных заказов, рассчитанный по количеству и единице цены таблицы Sales.OrderLines.

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

Я использую WorldWideImporters примеры таблиц базы данных Microsoft. Я пытаюсь вернуть 2015 sales information для каждого поставщика в Purchasing.Suppliers. Я возвращаю OrderCount и Sum of the 2015 sales в соответствующих столбцах. У меня возникли проблемы с joins, так как я должен подключить Suppliers к Warehouse.StockItems, а затем подключить эти элементы к определенному OrderLines, в котором есть поле для StockItemID.

Проблема в том, что обычно я присоединяю orders к orderlines, чтобы я мог фильтровать только orders и, следовательно, orderlines в 2015. Однако, с указанной выше структурой таблицы, мне кажется, что мне нужно подключить OrderLines к Orders.

Итак, я сделал join те Orders обратно с OrderLines, чтобы получить результат, к которому я привык. Вот моя попытка найти решение:

<code><pre>
SELECT S.SupplierID
      ,S.SupplierName
      ,COUNT(DISTINCT O.OrderID) AS OrderCount
      ,ISNULL(SUM(OLP.Quantity * OLP.UnitPrice), 0.00) AS Sales
FROM Purchasing.Suppliers AS S
LEFT OUTER JOIN Warehouse.StockItems AS W ON S.SupplierID = W.SupplierID
LEFT OUTER JOIN Sales.OrderLines AS OL ON W.StockItemID = OL.StockItemID
LEFT OUTER JOIN Sales.Orders AS O ON OL.OrderID = O.OrderID
                                  AND O.OrderDate BETWEEN '2015-01-01' AND '2015-12-31'
LEFT OUTER JOIN Sales.OrderLines AS OLP ON O.OrderID = OLP.OrderID
GROUP BY S.SupplierID
        ,S.SupplierName
ORDER BY Sales DESC
        ,OrderCount
        ,SupplierName;

Edit:

Результаты: Посмотрите на все supplier, как и ожидалось, даже те, которые не имели sales или orders. Я не уверен, правильно ли рассчитан sales, и я не уверен, как это проверить. Не знал, видел ли кто-нибудь недостаток в моем запросе.

Я понятия не имею, является ли это правильным или наиболее эффективным способом решения этой проблемы. У меня есть ограничения, которые я могу использовать только joins, нет subqueries, unions и т. Д.

Буду признателен за любую помощь в понимании. Спасибо.

1 Ответ

0 голосов
/ 18 сентября 2018

Для сравнения заказов без учета поставщиков или строк заказа:

/* query 1 */
SELECT 
       COUNT(*)         AS ordercount
FROM Sales.Orders AS o
WHERE o.OrderDate >= '20150101' AND o.OrderDate < '20160101'

Затем для сравнения строк заказов без учета поставщиков:

/* query 2 */
SELECT
       COUNT(DISTINCT o.OrderID)         AS ordercount
     , SUM(olp.Quantity * olp.UnitPrice) AS sales
FROM Sales.Orders AS o
        INNER JOIN Sales.OrderLines AS olp ON olP.OrderID = o.OrderID
WHERE o.OrderDate >= '20150101' AND o.OrderDate < '20160101'

Теперь начните вводить большеобъединяет, и если значения изменяются, то ошибается самое последнее объединение:

/* query 3 */
SELECT
       COUNT(DISTINCT o.OrderID)         AS ordercount
     , SUM(olp.Quantity * olp.UnitPrice) AS sales
FROM Sales.Orders AS o
        INNER JOIN Sales.OrderLines AS olp ON olP.OrderID = o.OrderID
        INNER JOIN Warehouse.StockItems AS w ON w.StockItemID = olp.StockItemID
WHERE o.OrderDate >= '20150101' AND o.OrderDate < '20160101'

, а затем:

/* query 4 */
SELECT
       COUNT(DISTINCT o.OrderID)         AS ordercount
     , SUM(olp.Quantity * olp.UnitPrice) AS sales
FROM Sales.Orders AS o
        INNER JOIN Sales.OrderLines AS olp ON olP.OrderID = o.OrderID
        INNER JOIN Warehouse.StockItems AS w ON w.StockItemID = olp.StockItemID
        INNER JOIN Purchasing.Suppliers s ON s.SupplierID = w.SupplierID
WHERE o.OrderDate >= '20150101' AND o.OrderDate < '20160101'

Вам определенно не нужно соединять строки заказа дважды, что касаетсяприсоединение слева зависит от того, чего вы пытаетесь достичь, например:

Только поставщики с заказами в диапазоне дат:

/* query 5 */
SELECT
    s.SupplierID
  , s.SupplierName
  , COUNT(DISTINCT o.OrderID)                       AS ordercount
  , ISNULL(SUM(olp.Quantity * olp.UnitPrice), 0.00) AS sales
FROM Purchasing.Suppliers s
    INNER JOIN Warehouse.StockItems AS w ON s.SupplierID = w.SupplierID
    INNER JOIN Sales.OrderLines AS olp ON w.StockItemID = olp.StockItemID
    INNER JOIN Sales.Orders AS o ON olP.OrderID = o.OrderID
WHERE o.OrderDate >= '20150101' 
    AND o.OrderDate < '20160101' -- note: this is "the next" day
GROUP BY
    s.SupplierID
  , s.SupplierName
ORDER BY
    sales DESC
  , ordercount
  , SupplierName;

Все поставщики со ссылками на запасы:

/* query 6 */
SELECT
    s.SupplierID
  , s.SupplierName
  , COUNT(DISTINCT o.OrderID)                       AS ordercount
  , ISNULL(SUM(olp.Quantity * olp.UnitPrice), 0.00) AS sales
FROM Purchasing.Suppliers s
    INNER JOIN Warehouse.StockItems AS w ON s.SupplierID = w.SupplierID
    LEFT JOIN Sales.OrderLines AS olp ON w.StockItemID = olp.StockItemID
    LEFT JOIN Sales.Orders AS o ON olP.OrderID = o.OrderID
                                AND o.OrderDate >= '20150101' 
                                AND o.OrderDate < '20160101' -- note: this is "the next" day
GROUP BY
    s.SupplierID
  , s.SupplierName
ORDER BY
    sales DESC
  , ordercount
  , SupplierName;

Каждый поставщик:

/* query 7 */
SELECT
    s.SupplierID
  , s.SupplierName
  , COUNT(DISTINCT o.OrderID)                       AS ordercount
  , ISNULL(SUM(olp.Quantity * olp.UnitPrice), 0.00) AS sales
FROM Purchasing.Suppliers s
    LEFT JOIN Warehouse.StockItems AS w ON s.SupplierID = w.SupplierID
    LEFT JOIN Sales.OrderLines AS olp ON w.StockItemID = olp.StockItemID
    LEFT JOIN Sales.Orders AS o ON olP.OrderID = o.OrderID
                                AND o.OrderDate >= '20150101' 
                                AND o.OrderDate < '20160101' -- note: this is "the next" day
GROUP BY
    s.SupplierID
  , s.SupplierName
ORDER BY
    sales DESC
  , ordercount
  , SupplierName;

Пожалуйста, будьте очень осторожны с использованием between для диапазонов дат. Самый надежный способ определить диапазон дат - это использовать >= и <как показано выше, таким образом, не имеет значения, какова точность данных во времени.Также YYYYMMDD - самый безопасный формат литералов даты в TSQL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...