Adding rows changes the sum result with group by - sql

How do I add more columns without changing the result?
Currently this script gets the top 300 purchase order results by order value using a sum & grouping by the order items oi.nettprice This shows the correct results as shown below.
Results shown here
select top 300
oi.orderid, o.traderid, o.orderdate,
SUM(oi.nettprice) AS nettprice
from orderitems AS oi
INNER JOIN orders AS o ON o.tradertype = 'S' AND o.id = oi.orderid
where oi.ordertype = 'PO'
AND oi.status != 'CANCELLED'
group by
oi.orderid, o.traderid, o.orderdate
order by
nettprice DESC
Adding any additional columns to show more information changes the results and alters the sum of oi.nettprice as shown below, I've tried to use group by rollup but this gives the value in a separate row with the other column values as NULL.
select top 300
oi.orderid, o.traderid,
o.orderdate, oi.partid, oi.description, oi.partrevisionid,
SUM(oi.nettprice) AS nettprice
from orderitems AS oi
INNER JOIN orders AS o ON o.tradertype = 'S' AND o.id = oi.orderid
group by rollup
(oi.orderid, o.traderid, o.orderdate, oi.ordertype, oi.status, oi.partid, oi.description,
oi.partrevisionid)
HAVING oi.ordertype ='PO'
AND oi.status != 'CANCELLED'
order by
nettprice DESC
Results using Group by rollup
How can I clear this up to include columns from either table with the correct total (SUM) for each purchase order without affecting the results?

Use sum() partition by to get the desired result
SELECT top 300 oi.orderid,
o.traderid,
o.orderdate,
oi.partid,
oi.description,
oi.partrevisionid,
SUM(oi.nettprice) OVER(PARTITION BY oi.orderid, o.traderid, o.orderdate) AS nettprice
FROM orderitems AS oi
INNER JOIN orders AS o ON o.tradertype = 'S'
AND o.id = oi.orderid
WHERE oi.ordertype = 'PO'
AND oi.status != 'CANCELLED'
ORDER BY nettprice DESC

Related

How to select top 300 for each orders total order item value

I want to view the top 300 items ordered by total net price, how do I do this please?
Using SSMS 2014
If I remove group by I get error: Column 'orderitems.orderid' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Please see workings below:
select top 300
orderitems.orderid, orders.traderid, orders.orderdate,
SUM(orderitems.nettprice) AS nettprice
from orderitems
INNER JOIN orders ON orders.tradertype = 'S' AND orders.id =
orderitems.orderid
where orderitems.ordertype = 'PO'
group by orderitems.orderid, orders.traderid, orders.orderdate,
orderitems.nettprice
order by orderitems.nettprice DESC
You need to order by the SUM value. You can either put that in the ORDER BY explicitly, or you can use the SELECT column name without a table reference (in other words the column alias you use in the SELECT)
I strongly recommend you use short table aliases to make your code more readable
select top 300
oi.orderid,
o.traderid,
o.orderdate,
SUM(oi.nettprice) AS nettprice
from orderitems AS oi
INNER JOIN orders AS o ON o.tradertype = 'S' AND o.id = oi.orderid
where oi.ordertype = 'PO'
group by
oi.orderid, o.traderid, o.orderdate
order by
nettprice DESC
-- alternatively
order by
SUM(oi.nettprice) DESC
Perhaps what you need here is a windowed SUM and then a TOP. The PARTITION BY clause is based on this comment:
SELECT TOP (300)
oi.orderid,
o.traderid,
o.orderdate,
SUM(oi.nettprice) OVER (PARTITION BY oi.itemnumber, oi.partid, oi.quantity) AS totalnettprice
FROM dbo.orderitems oi
INNER JOIN dbo.orders o ON o.id = oi.orderid
WHERE o.tradertype = 'S'
AND oi.ordertype = 'PO'
ORDER BY oi.totalnettprice DESC;
Very simple:
select top 300
itm.orderid,
ord.traderid,
ord.orderdate,
SUM(itm.nettprice) AS price
from orderitems itm
INNER JOIN orders ord ON
ord.tradertype = 'S' AND ord.id = itm.orderid
where itm.ordertype = 'PO'
group by
itm.orderid,
ord.traderid,
ord.orderdate
order by price DESC

How do I make the GROUP BY clause in this query run?

Write a SELECT statement that returns three columns:
EmailAddress, OrderID, and the order total for each customer.
To do this, you can group the result set by the EmailAddress and OrderID columns.
In addition, you must calculate the order total from the columns in the OrderItems table.
SELECT c.EmailAddress, oi.OrderID, (oi.ItemPrice * oi.Quantity) -
oi.DiscountAmount
FROM Customers c JOIN Orders o ON c.CustomerID = o.CustomerID
JOIN OrderItems oi ON o.OrderID = oi.OrderID
GROUP BY c.EmailAddress, oi.OrderID
You are looking for GROUP BY and SUM():
SELECT c.EmailAddress, oi.OrderID,
SUM(oi.ItemPrice * oi.Quantity - oi.DiscountAmount)
FROM Customers c JOIN
Orders o
ON c.CustomerID = o.CustomerID JOIN
OrderItems oi
ON o.OrderID = oi.OrderID
GROUP BY c.EmailAddress, oi.OrderID;
Column must appear in the GROUP BY clause or be used in an aggregate function. Your column
(oi.ItemPrice * oi.Quantity) - oi.DiscountAmount
does not satisfy this requirement. I assume what you want to do is
sum((oi.ItemPrice * oi.Quantity) - oi.DiscountAmount)
?

Left join returning bad values

I'm not very good with SQL queries but I attempted to write this one:
SELECT DATEPART(YY,Orders.OrderDate) as Year,
DATEPART(MM,Orders.OrderDate) as Month,
(SUM(case when OrderDetails.ProductCode = 'XXX' then
OrderDetails.ProductPrice else 0 end) + SUM(Orders.Total))
AS XXX
FROM Orders
LEFT JOIN OrderDetails ON Orders.OrderID = OrderDetails.OrderID
WHERE Orders.OrderStatus = 'Shipped'
GROUP BY DATEPART(MM,Orders.OrderDate), DATEPART(YY,Orders.OrderDate)
ORDER BY DATEPART(YY,Orders.OrderDate),DATEPART(MM,Orders.OrderDate)
The OrderDetails is linked to the Orders table by the field OrderID. In this SELECT query I'm trying to get the SUM of OrderDetails.ProductPrice when the OrderDetails.ProductCode is XXX and add it to the Orders.Total to get total amounts for each month/year.
The query is working except for one problem (that's probably either a amateur mistake or has been worked around several times). When performing the LEFT JOIN, the OrderDetails table can have multiple records linked to the Orders table which is throwing bad results in the SUM(Orders.Total). I've isolated that issue I just can't seem to fix it.
Can anybody point me in the right direction?
If we assume that the XXX product only appears at most once for each order, then this should work:
SELECT year(o.OrderDate) as Year, month(o.OrderDate) as Month,
(COALESCE(SUM(od.ProductPrice), 0) + SUM(o.Total)) AS XXX
FROM Orders o LEFT JOIN
OrderDetails od
ON o.OrderID = od.OrderID AND od.ProductCode = 'XXX'
WHERE o.OrderStatus = 'Shipped'
GROUP BY year(o.OrderDate), month(o.OrderDate)
ORDER BY year(o.OrderDate), month(o.OrderDate);
If it can appear multiple times, then move that part of the aggregation to a subquery:
SELECT year(o.OrderDate) as Year, month(o.OrderDate) as Month,
(COALESCE(XXX, 0) + SUM(o.Total)) AS XXX
FROM Orders o LEFT JOIN
(SELECT od.OrderId, SUM(od.ProductPrice) as XXX
FROM OrderDetails od
WHERE od.ProductCode = 'XXX'
GROUP BY od.OrderId
) od
ON o.OrderID = od.OrderID
WHERE o.OrderStatus = 'Shipped'
GROUP BY year(o.OrderDate), month(o.OrderDate)
ORDER BY year(o.OrderDate), month(o.OrderDate);

SQL: list items not sold in between time period

The software we use has two tables for orders, Orders and OrderItems.
I am trying do run a query on the database to show which items have not sold in a period of time.
This is what I have but It's not working ( it brings up all the records)
SELECT
OrderItem.Name,
OrderItem.SKU,
[Order].OrderDate
FROM
[Order]
INNER JOIN OrderItem ON [Order].OrderID = OrderItem.OrderID
WHERE
(OrderItem.SKU NOT IN
(SELECT DISTINCT OrderItem.SKU WHERE ([Order].OrderDate BETWEEN '2014-09-08' AND '2014-01-01')))
You can actually do this with a having clause:
SELECT oi.Name, oi.SKU, max(o.OrderDate) as lastOrderDate
FROM [Order] o INNER JOIN
OrderItem oi
ON o.OrderID = oi.OrderID
GROUP BY oi.Name, oi.SKU
HAVING sum(case when o.OrderDate between '2014-01-01' and '2014-09-08' then 1 else 0 end) = 0;
If you are just looking for orders before this year, it is easier to write the having clause as:
HAVING max(o.OrderDate) < '2014-01-01'
Flip your dates. It should be [Begin Date] Between [End Date]
WHERE (OrderItem.SKU NOT IN
(SELECT DISTINCT OrderItem.SKU
WHERE ([Order].OrderDate BETWEEN '2014-01-01' AND '2014-09-08')))
How about the following:
SELECT oi.Name, oi.SKU, o.OrderDate
FROM [Order] o
INNER JOIN OrderItem oi ON o.OrderID = oi.OrderID
WHERE oi.SKU NOT IN
(
SELECT os.SKU
FROM [Order] os
INNER JOIN OrderItem ois ON os.OrderID = ois.OrderID
WHERE os.OrderDate BETWEEN '2014-01-01' AND '2014-09-08'
)
You have to join to the OrderItem table in the sub query in order to get the SKU.
Looks like your query was build 'upside down'.
SELECT DISTINCT SKU FROM OrderItem oi
WHERE NOT EXISTS
(SELECT 1 FROM Order o
JOIN OrderItem oi2 ON o.OrderID = oi2.OrderID
WHERE oi2.SKU = oi.SKU
AND o.OrderDate BETWEEN '2014-01-01' AND '2014-09-08' );
Ideally, you should have another table containing your distinct items, so you could write the following query to see which items were not sold during a certain period (and may have never been sold at all).
select i.SKU from items
where not exists (
select 1 from OrderItem oi
join Order o on o.OrderID = oi.OrderID
where oi.SKU = i.SKU
and o.OrderDate BETWEEN '2014-01-01' and '2014-09-08'
)
if you don't have such a table, you can select all products that have been ordered at some point, but not during another period
select i.SKU from (
select distinct oi.SKU from OrderItem oi
) i
where not exists (
select 1 from OrderItem oi
join Order o on o.OrderID = oi.OrderID
where oi.SKU = i.SKU
and o.OrderDate BETWEEN '2014-01-01' and '2014-09-08'
)

sum over partition

The following query bring the correct value for totalqty. however it leaves or misses counting the qty for some items, why?
SELECT
PRODID, ITEMDES, QTY, SUM(QTY) over (partition by prodId) as totalqty, StockCode,shipName, shipCompany, shipAddress1, shipAddress2, shipAddress3,shipPostCode,shipcity,shipCountry,shipCounty,customerMessage
FROM orderedItems oi
left join orders o on oi.order_id = o.order_id
WHERE prodId = prodId
AND o.status = 'transaction authorised'
AND o.delTime = '#FORM.delDateselect#'
Group by PRODID,ITEMDES,QTY, StockCode,shipName, shipCompany, shipAddress1, shipAddress2, shipAddress3,shipPostCode,shipcity,shipCountry,shipCounty,customerMessage
ORDER BY PRODID
Your where clause makes the left outer join behave like an inner join. Change it like this:
WHERE
prodId = prodId and
(
o.order_id is null or
(
o.status = 'transaction authorised' AND
o.delTime = '#FORM.delDateselect#'
)
)
The reason is that if there is no matching order in orders o.status will be NULL and thus not equal to 'transaction authorised'.
Furthermore, you don't need the group by. You already have the analytical function that does the SUM for you.