SQL重复外部联接以提高效率

时间:2018-09-17 22:56:52

标签: sql-server tsql join

Probelm声明:

  

编写查询以返回每个供应商的2015年销售信息。我们希望将所有供应商都包括在结果集中,无论他们的产品是否在2015年出售。

     

使用前两个问题中的Sales.Orders和Sales.OrderLines确定销售。但是,由于我们是从供应商的角度要求此信息的,因此您还需要使用表Warehouse.StockItems和Purchasing.Suppliers。

     

结果集中所需的列为:

     

SupplierID -出现在表Purchasing.Suppliers中。
   SupplierName -出现在表Purchasing.Suppliers中。
   OrderCount -每个供应商在产品上下的订单数。
  销售-下订单的小计,根据表Sales.OrderLines的数量和单价计算。

     

应该对结果进行排序,以使销售额最高的供应商位于顶部。如果两个供应商的销售额相同,则下一个使用最高订单数的订单数。如果两个供应商的销售和订单数量相同,请使用升序的供应商名称作为最终的平局。这将确保确定的结果。

我正在使用WorldWideImporters Microsoft示例数据库表。我试图返回2015 sales information中每个供应商的Purchasing.Suppliers。我将在相应的列中返回OrderCountSum of the 2015 sales。我在这里遇到joins的问题,因为我必须将Suppliers连接到Warehouse.StockItems,然后将这些项目连接到具有OrderLines字段的特定StockItemID

问题在于,通常我会将orders加入orderlines,这样我只能过滤orders中的orderlines中的2015。但是,使用我指定的表结构,似乎必须将OrderLines连接到Orders

所以我要做的是将join的那些Orders退回OrderLines,以提供我习惯的结果。这是我尝试的解决方案:

<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;
</pre>

编辑:

结果: 希望所有supplier都没有期望,甚至没有salesorders的也是如此。我不确定所计算的sales是否正确,也不确定如何验证。不知道是否有人在我的查询中发现缺陷。

我不知道这是正确的还是解决此问题的最有效方法。我确实有限制,只能使用joins,不能使用subqueriesunions等。

在理解方面的任何帮助将不胜感激。谢谢。

1 个答案:

答案 0 :(得分:0)

要在不考虑供应商或订单行的情况下对订单进行基准测试:

/* 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中最安全的日期文字格式。