选择子查询分组依据

时间:2018-10-26 23:08:07

标签: sql sql-server subquery

这个简单的查询存在问题。我收到错误

“子查询返回的值超过1。当子查询遵循=,!=,<,<=,>,> =或将子查询用作表达式时,不允许这样做。”

    SELECT TOP 100 PERCENT 
         dbo.Inventory.PARTNO
        ,( SELECT ISNULL(SUM(dbo.PurchaseOrderReceived.QtyReceived), 0)
             FROM dbo.PurchaseOrderReceived 
             JOIN dbo.PurchaseOrderlineItems 
               ON dbo.PurchaseOrderReceived.POLIID = dbo.PurchaseOrderlineItems.POLIID 
             JOIN dbo.Inventory 
               ON dbo.PurchaseOrderlineItems.InvMasID = dbo.Inventory.InvMasID 
             JOIN dbo.Duties Duties_1 ON dbo.Inventory.DutyClass = Duties_1.DutyID
            WHERE (Duties_1.DutyClass = 252) 
              AND (dbo.PurchaseOrderlineItems.Deleted = 0)
              AND (dbo.PurchaseOrderReceived.ReceivedDate > CONVERT(DATETIME, '2018-08-22 00:00:00', 102))
         GROUP BY dbo.Inventory.PARTNO
        ) AS Purchased
       ,ISNULL(SUM(dbo.OrderItems.QtyShipped), 0) AS Ordered
       ,ISNULL(SUM(DISTINCT dbo.MRP.QtyOnHand), 0) AS QOH
  FROM dbo.Orders 
  JOIN dbo.OrderItems
  JOIN dbo.Inventory 
    ON dbo.OrderItems.InvMasID = dbo.Inventory.InvMasID 
    ON dbo.Orders.OrderID = dbo.OrderItems.OrderID
  JOIN dbo.MRP ON dbo.Inventory.InvMasID = dbo.MRP.InvMasID
  JOIN dbo.Duties ON dbo.Inventory.DutyClass = dbo.Duties.DutyID

 WHERE (dbo.Orders.ShipDate > CONVERT(DATETIME, '2018-08-22 00:00:00', 102)) 
   AND (dbo.Duties.DutyClass = 252)
 GROUP BY dbo.Inventory.PARTNO

我已经尝试解决此问题,并且知道我缺少一个简单的解决方案。与主查询分开时,子查询本身会检索我要查找的信息。谢谢你的帮助!

5 个答案:

答案 0 :(得分:1)

我只能在上面的查询中看到此子查询

SELECT ISNULL(SUM(dbo.PurchaseOrderReceived.QtyReceived),0)
    FROM dbo.PurchaseOrderReceived
      INNER JOIN dbo.PurchaseOrderlineItems ON dbo.PurchaseOrderReceived.POLIID = dbo.PurchaseOrderlineItems.POLIID
      INNER JOIN dbo.Inventory ON dbo.PurchaseOrderlineItems.InvMasID = dbo.Inventory.InvMasID
      INNER JOIN dbo.Duties Duties_1 ON dbo.Inventory.DutyClass = Duties_1.DutyID
    WHERE (Duties_1.DutyClass = 252)
    AND   (dbo.PurchaseOrderlineItems.Deleted = 0)
    AND   (dbo.PurchaseOrderReceived.ReceivedDate > CONVERT(DATETIME,'2018-08-22 00:00:00',102))
    GROUP BY dbo.Inventory.PARTNO

按PARTNO分组,应该返回多个值。确保不超过一个满足您whereinner join条件的条件的零件编号

答案 1 :(得分:1)

子查询需要返回一个值,而在您的子查询中,它返回了多个值,这是因为PARTNO内部的组。您可以删除该组,或者使用TOP 1,或者仅包含带有SUM,MAX,MIN的子查询来解决该问题。

另一件事,由于子查询也使用相同和相对的源,因此您可以将它们连接起来(这会更好)。

类似的东西:

SELECT TOP 100 PERCENT 
    dbo.Inventory.PARTNO
,   ISNULL(SUM(dbo.por.QtyReceived), 0) AS Purchased
,   ISNULL(SUM(dbo.OrderItems.QtyShipped), 0) AS Ordered
,   ISNULL(SUM(DISTINCT dbo.MRP.QtyOnHand), 0) AS QOH
FROM dbo.Orders 
JOIN dbo.OrderItems ON dbo.Orders.OrderID = dbo.OrderItems.OrderID
JOIN dbo.Inventory ON dbo.OrderItems.InvMasID = dbo.Inventory.InvMasID 
JOIN dbo.MRP ON dbo.Inventory.InvMasID = dbo.MRP.InvMasID
JOIN dbo.Duties ON dbo.Inventory.DutyClass = dbo.Duties.DutyID
LEFT JOIN dbo.PurchaseOrderlineItems poli ON poli.InvMasID = dbo.Inventory.InvMasID AND poli.Deleted = 0
LEFT JOIN dbo.PurchaseOrderReceived por ON por.POLIID = poli.POLIID AND (dbo.por.ReceivedDate > CONVERT(DATETIME, '2018-08-22 00:00:00', 102))
WHERE 
    dbo.Orders.ShipDate > CONVERT(DATETIME, '2018-08-22 00:00:00', 102)
AND (dbo.Duties.DutyClass = 252)
GROUP BY dbo.Inventory.PARTNO

由于您没有提供任何样本,因此我无法验证结果,但我认为足以证明这种方法。

答案 2 :(得分:1)

简单的解决方案似乎是在子查询或主SELECT语句中为清单表取别名,并通过主查询中的PARTNO过滤子查询中的记录。我认为反正还是一个好主意:

SELECT TOP 100 PERCENT
    i.PARTNO
  , ( SELECT ISNULL(SUM(por.QtyReceived), 0)
      FROM dbo.PurchaseOrderReceived por
        JOIN dbo.PurchaseOrderlineItems poli ON por.POLIID = poli.POLIID
          JOIN dbo.Inventory i1 ON poli.InvMasID = i1.InvMasID
            JOIN dbo.Duties d1 ON i1.DutyClass = d1.DutyID
        WHERE (d1.DutyClass = 252)
          AND (i1.PARTNO = i.PARTNO)
          AND (poli.Deleted = 0)
          AND (por.ReceivedDate > CONVERT(DATETIME, '2018-08-22 00:00:00', 102))
      ) AS Purchased
  , ISNULL(SUM(oi.QtyShipped), 0) AS Ordered
  , ISNULL(SUM(DISTINCT m.QtyOnHand), 0) AS QOH
FROM dbo.Orders o
  JOIN dbo.OrderItems oi ON o.OrderID = oi.OrderID
    JOIN dbo.Inventory i ON oi.InvMasID = i.InvMasID 
      JOIN dbo.MRP m ON i.InvMasID = m.InvMasID
      JOIN dbo.Duties d ON i.DutyClass = d.DutyID
WHERE (o.ShipDate > CONVERT(DATETIME, '2018-08-22 00:00:00', 102)) 
  AND (d.DutyClass = 252)
GROUP BY i.PARTNO

答案 3 :(得分:1)

该重新开始并应用一些最佳编码实践了。您的加入是一种“非典型”方式,许多人都难以理解。混淆的代码很可能导致错误。移动您的联接子句以遵循实际的联接表。例如,您有

FROM dbo.Orders 
  JOIN dbo.OrderItems
  JOIN dbo.Inventory 
    ON dbo.OrderItems.InvMasID = dbo.Inventory.InvMasID 
    ON dbo.Orders.OrderID = dbo.OrderItems.OrderID
  JOIN dbo.MRP ON dbo.Inventory.InvMasID = dbo.MRP.InvMasID
  JOIN dbo.Duties ON dbo.Inventory.DutyClass = dbo.Duties.DutyID

您没有通过关联的ON子句第一次跟随OrderItems进行联接-在连接到Inventory之后,它以某种方式结束。奇怪的是,加入MRP的on子句确实紧随该表之后。不一致是不良的开发人员习惯。 IMO比“非典型”地做事更糟。最后,使用别名来减少混乱并使代码更易于阅读。易于阅读意味着您的代码更容易被理解。改进的版本是:

FROM dbo.Orders as Ord 
JOIN dbo.OrderItems as Itm on Ord.OrderID = Itm.OrderID
JOIN dbo.Inventory as Inv on Itm.InvMasID = Inv.InvMasID
JOIN dbo.MRP as MRP ON Inv.InvMasID = MRP.InvMasID
JOIN dbo.Duties as Duties ON Inv.DutyClass = Duties.DutyID

您最好对表进行模式限定-很少看到张贴者这样做。但是,这也会使名称变得很长,这是建议使用别名的原因之一。

接下来,您有“选择前100%” 。每当您认为需要此功能时,请停止编写代码,然后再做其他事情。 从不有用。如果SSMS试图通过添加来“修复”某些东西,请不要相信。

您要发布的问题是子查询。您试图“解决”原始问题,但没有告诉我们该问题是什么。这是查询返回了错误的求和值。为什么这样做呢?因为您没有将子查询与外部查询相关联,所以它为每个库存清单行计算了一个值。

为此,您需要在子查询中删除对库存的联接。为什么?因为适当的库存行的关键字将来自外部查询-这就是将内部与外部相关联的方式。将没有group by子句,因为您的目标是为外部查询中的每一行计算一个值。从逻辑上讲,group by子句表明您打算在该查询中产生多行-这不是您想要发生的(并且不会发生,因为引擎将在这种情况下产生错误-您已发现)。其他一些问题:

  • (显然)别名问题,使子查询难以阅读。
  • 在当前位置可能不需要使用isull,但在其他地方则需要使用isull。子查询总是有可能找不到匹配的行并返回空值。如果您不希望空值,则将整个子查询都用isull包装。因为这样做,您不需要在当前拥有它的地方使用它。而且,您当前拥有的位置对于“匹配”情况可能是不需要的-但我不能在不了解您的架构的情况下肯定地说。没关系,正确的放置将使求和计算周围不需要它。
  • where子句中的重复条件。一旦正确建立关联,就不需要此操作。

因此,应用所有这些更改应该会留下类似的内容:

,( SELECT SUM(POR.QtyReceived) 
             FROM dbo.PurchaseOrderReceived as POR 
             JOIN dbo.PurchaseOrderlineItems as POItm
               ON POR.POLIID = POItm.POLIID 
            WHERE Inv.InvMasID = POItm.InvMasID --this is the correlation 
              AND POItm.Deleted = 0 
              AND POR.ReceivedDate > CONVERT(DATETIME, '2018-08-22 00:00:00', 102))
       ) AS Purchased

希望我没有添加任何错别字或错误-很明显我自己无法检查此问题。但这至少可以为您提供一个更好的起点。您对日期的使用似乎令人怀疑,但是-再次-我不知道您的架构或数据,其使用方式或您要查询的内容。

答案 4 :(得分:0)

输入此条件,并且dbo.Orders.PARTNO = dbo.Inventory.PARTNO 和前1名

SELECT TOP 100 PERCENT 
         dbo.Inventory.PARTNO
        ,( SELECT Top 1 ISNULL(SUM(dbo.PurchaseOrderReceived.QtyReceived), 0)
             FROM dbo.PurchaseOr,derReceived 
             JOIN dbo.PurchaseOrderlineItems 
               ON dbo.PurchaseOrderReceived.POLIID = dbo.PurchaseOrderlineItems.POLIID 
             JOIN dbo.Inventory 
               ON dbo.PurchaseOrderlineItems.InvMasID = dbo.Inventory.InvMasID 
             JOIN dbo.Duties Duties_1 ON dbo.Inventory.DutyClass = Duties_1.DutyID
            WHERE (Duties_1.DutyClass = 252) 
              AND (dbo.PurchaseOrderlineItems.Deleted = 0)
              AND (dbo.PurchaseOrderReceived.ReceivedDate > CONVERT(DATETIME, '2018-08-22 00:00:00', 102)) 
AND dbo.Orders.PARTNO = dbo.Inventory.PARTNO
         GROUP BY dbo.Inventory.PARTNO
        ) AS Purchased
       ,ISNULL(SUM(dbo.OrderItems.QtyShipped), 0) AS Ordered
       ,ISNULL(SUM(DISTINCT dbo.MRP.QtyOnHand), 0) AS QOH
  FROM dbo.Orders 
  JOIN dbo.OrderItems
  JOIN dbo.Inventory 
    ON dbo.OrderItems.InvMasID = dbo.Inventory.InvMasID 
    ON dbo.Orders.OrderID = dbo.OrderItems.OrderID
  JOIN dbo.MRP ON dbo.Inventory.InvMasID = dbo.MRP.InvMasID
  JOIN dbo.Duties ON dbo.Inventory.DutyClass = dbo.Duties.DutyID

 WHERE (dbo.Orders.ShipDate > CONVERT(DATETIME, '2018-08-22 00:00:00', 102)) 
   AND (dbo.Duties.DutyClass = 252)
 GROUP BY dbo.Inventory.PARTNO