Select Subquery Group By

南笙酒味 提交于 2019-12-25 00:33:40

问题


I am having an issue with this simple query. I get the error

"Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <=, >, >= or when the subquery is used as an expression."

    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

I have tried working through this problem and know that there is a simple solution I am missing. The subquery, on its own, retrieves the information I am looking for, as does the main query, when separated. Thanks for any help!


回答1:


put this condition AND dbo.Orders.PARTNO = dbo.Inventory.PARTNO and Top 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



回答2:


I can only see this sub query in your query above

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

Grouped by PARTNO this is supposed to return multiple values. Make sure that there are not more than one part No that satisfies the condition on your where and inner join conditions




回答3:


The sub query needs to return one value, while in yours it returned multiple values, that's because the inner group by PARTNO. Either you remove that group by, or use TOP 1, or just contain the sub query with SUM,MAX,MIN to solve it.

Another thing, since your sub-query is also using the same and relative sources, you can join them instead (which will be better).

Something like this :

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

I can't verify the results since you didn't provide any samples, but I think it'll be good enough to show that approach.




回答4:


The simple solution seems to be aliasing the inventory table in either the subquery or in the main SELECT statement and filter the records in the subquery by PARTNO from the main query. I think that aliasing is anyway a good idea:

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



回答5:


Time to start over and apply some best coding practices. Your joining is structured in an "atypical" fashion that many will struggle to understand. And code that confuses is likely to lead to mistakes. Move your join clauses to follow the actual joined tables. E.g., you have

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

You did not follow the first join to OrderItems with the associated ON clause - somehow it wound up after the join to Inventory. Oddly, the on clause for the join to MRP does immediately follow that table. Inconsistency is a bad developer habit. IMO worse than doing things "atypically". And lastly, use alias to reduce the clutter and make your code easier to read. Easier to read means your code is more likely to be understood. The improved version is:

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

It is good that you schema-qualify your tables - it is rare to see posters do that. However, it also makes the names very long which is one reason why the use of an alias is recommended.

Next, you have "select top 100 percent". Whenever you think you need this, stop writing code and go do something else for a bit. This is NEVER useful. Don't believe SSMS if it tries to "fix" something by adding it.

The problem you are posting about is the subquery. You tried to "fix" the original problem but you did not tell us what that problem was. It was that the query returned the wrong summed value. Why did it do that? Because you did not correlate the subquery to the outer query so that it calculated a value for each individual inventory row.

To do that, you need to remove the join to Inventory in the subquery. Why? Because the key to the appropriate inventory row will come from the outer query - that is how you correlate the inner with the outer. There will be no group by clause because your goal is to compute a single value for each row in the outer query. The group by clause logically indicates that you intend to produce multiple rows in that query - which is not what you want to happen (and cannot happen because the engine will produce an error in that situation - which you have discovered). Some additional problems:

  • The alias issue (obviously) which makes the subquery difficult to read.
  • The use of isnull is likely not needed where currently located but is needed elsewhere. It is always possible that a subquery will find no matching rows and return a null value. You wrap the entire subquery with isnull if you don't want null values. Because you do this, you don't need it where you currently have it. And where you currently have it is likely not needed for "matching" situations - but I can't say that with certainty without knowing your schema. No matter, the correct placement will negate the need for it around the sum computation.
  • Duplicative conditions in the where clause. This is not needed once you correlate correctly.

So applying all those changes should leave something like:

,( 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

Hopefully I did not add any typos or errors - I obviously cannot check this myself. But that at least gives you an improved starting point. Your use of dates looks suspect but - again - I don't know your schema or your data or how it is used or what you query is intended to do.



来源:https://stackoverflow.com/questions/53017310/select-subquery-group-by

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!