【问题标题】:SQL Repeated Outer Join Improving EfficiencySQL重复外连接提高效率
【发布时间】:2018-09-17 22:56:52
【问题描述】:

问题陈述:

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

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

结果集中需要的列是:

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

应对结果进行排序,以使销售额最高的供应商位于顶部。如果两个供应商的销售额相同,则接下来使用顶部计数最高的订单计数。如果两个供应商的销售额和订单数相同,则使用供应商名称升序排列作为最后的决胜局。这将确保一个确定性的结果。

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

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

所以我所做的就是将join 那些OrdersOrderLines 一起返回以提供我习惯的结果。这是我的解决方案尝试:

<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 等。

任何有助于理解的帮助将不胜感激。谢谢。

【问题讨论】:

  • 你应该用源表标记每一列。
  • 请阅读minimal reproducible example并采取行动。表明您的程序在通过(子)表达式时计算出您期望的结果——包括传递给每个函数/运算符的参数是否满足其要求——通过说明这是什么并表明它实际上是通过增量输出完成的。找到 MCVE 的一部分是找到部分功能的最大工作示例。询问示例之间的(小)差异。请阅读并按照谷歌搜索“stackexhange 作业”的点击量采取行动。 PS“将订单连接到订单行”与“将订单行连接到订单”有何不同?
  • 请在您的问题中添加您正在使用的链接。但也请让您的帖子独立,包括 DDL、示例输入和所需输出等。链接死了。 PS您不清楚重新匹配的行如何影响结果。我们无法从您的查询中推断出,因为您没有说它是正确的。当然,我们可以猜测。但能够清楚地表达这一点是解决方案编程的一部分并给出minimal reproducible example。另外,what does "efficient" mean? 另见Why is “Can someone help me?” not an actual question?
  • 嗨。了解 LEFT JOIN 返回的内容:INNER JOIN 行加上由 NULL 扩展的不匹配的左表行。作为 LEFT JOIN 的一部分,始终知道您想要什么 INNER JOIN。在您拥有的 LEFT JOIN 之后,需要右表列不为 NULL 的 WHERE 或 ON 删除任何由 NULL 扩展的行,即只留下 INNER JOIN 行,即“将 LEFT JOIN 转换为 INNER JOIN”。
  • @philipxy 我已经更新了我的问题。我对这两个 SQL 和这个论坛都是新手,所以请给我反馈。谢谢

标签: sql-server tsql join


【解决方案1】:

对订单进行基准测试,而不考虑供应商或订单行:

/* 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作为日期范围,定义日期范围最可靠的方法是使用&gt;=&lt;,如上图所示,这样无论时间精度如何数据是。 YYYYMMDD 也是 TSQL 中最安全的日期文字格式。

【讨论】:

  • 您的查询不包括没有订单和/或没有销售的供应商。这就是我进行左连接的原因。此外,您计算的销售额与我的不同,我不确定哪个是正确的
  • @RobWantsToLearn 但是正如我所说,您的问题既没有清楚地解释也没有给出所需输出的示例。 “我不确定计算出的销售额是否正确,也不知道如何验证。”查找具有各种情况的某些情况的输入数据,并手动和/或查询和发布所需的输出数据。至少通过计算所需输出的方式来描述您想要什么。最好的方法是给出一个测试/条件,说明给定的值行(供应商名称、供应商名称、订单计数、销售额)是否出现在结果中——就情况和/或基表而言。
  • 如果您需要列出所有供应商,您可以介绍left joins,我不清楚这就是您需要的。关于计算的差异,通过将订单线连接两次,您将行数相乘。
  • 您最后两次查询的结果不正确,表明一家公司有0个订单但有销售额。销售额也太高了,让我相信它是在计算所有年份的总销售额。第一个内部连接查询为销售提供了更可信的结果,但不包括具有空订单数和销售额的供应商(因为它不应该)我很难找出正确的查询。
  • 经过进一步检查,OrderCount 似乎可能是错误的。当我在 2015 年使用两个不同的单独订单查询合计订单时,我得到了 23,329 个订单。通过我们正在进行的查询,我们得到了 53,292 个订单。所以看起来我们可能查询的不仅仅是 2015 年的订单。
猜你喜欢
  • 1970-01-01
  • 2021-09-22
  • 2015-07-20
  • 1970-01-01
  • 2019-05-06
  • 2022-01-19
  • 1970-01-01
  • 2012-02-02
  • 2020-07-28
相关资源
最近更新 更多