select count over partition by - sql-server

I am learning window functions in sql server. I am using AdventrueWorks2012 database for practice. I want to calculate total number of sales and purchases for each item in the store.
The classic solution can be like
SELECT ProductID,
Quantity,
(SELECT Count(*)
FROM AdventureWorks.Purchasing.PurchaseOrderDetail
WHERE PurchaseOrderDetail.ProductID = p.ProductID) TotalPurchases,
(SELECT Count(*)
FROM AdventureWorks.Sales.SalesOrderDetail
WHERE SalesOrderDetail.ProductID = p.ProductID) TotalSales
FROM (SELECT DISTINCT ProductID,
Quantity
FROM AdventureWorks.Production.ProductInventory) p
Trying to convert to window functions gives me wrong results:
SELECT DISTINCT d.ProductID,
Quantity,
Count(d.ProductID)
OVER(
PARTITION BY d.ProductID) TotalPurchases,
Count(d2.ProductID)
OVER(
PARTITION BY d2.ProductID) TotalSales
FROM (SELECT DISTINCT ProductID,
Quantity
FROM AdventureWorks.Production.ProductInventory) p
INNER JOIN AdventureWorks.Purchasing.PurchaseOrderDetail d
ON p.ProductID = d.ProductID
INNER JOIN AdventureWorks.Sales.SalesOrderDetail d2
ON p.ProductID = d2.ProductID
ORDER BY d.ProductID
Why this is wrong? How can I correct it?

You should change INNER JOIN to LEFT JOIN
Because when you inner join, result will miss productid which from ProductInventory table does not have PurchaseOrderDetail or SalesOrderDetail.

Related

Is there a way to UPDATE TOP (N) with inner join where N is a field of such inner join?

I'm trying to create a script that synchronizes Sales and Inventory tables. For that I wrote an UPDATE on the Inventory table (which has 1 record per item of inventory present) like this:
UPDATE TOP (q.QuantitySold) i
SET i.Converted = 1,
i.CartID = q.CartID,
i.ReservedDate = GETDATE()
FROM Inventory i
INNER JOIN
(
SELECT product.ProductID, sales.CartID, COUNT(sales.ID) AS QuantitySold
FROM Products product
INNER JOIN Sales sales ON sales.ProductID = product.ProductID
WHERE <conditions>
GROUP BY product.ProductID, sales.CartID
) q ON q.ProductID = i.ProductID
WHERE i.Converted = 0 AND i.CartID IS NULL
But it's not working, error says q.QuantitySold couldn't be bound.
Is there a way to update N records of inventory (equal to the quantity sold) without using a cursor? I refuse to give up like that.
Note: this is a simplified version of the actual query.
You could use ROW_NUMBER to enumerate the inventory items that you need to update.
WITH cteProducts AS(
SELECT product.ProductID, sales.CartID, COUNT(sales.ID) AS QuantitySold
FROM Products product
INNER JOIN Sales sales ON sales.ProductID = product.ProductID
WHERE <conditions>
GROUP BY product.ProductID, sales.CartID
),
cteInventory AS(
SELECT *,
ROW_NUMBER() OVER( PARTITION BY ProductID ORDER BY (SELECT NULL)) AS rn /*Change the ORDER BY for an actual column if needed, probably for FIFO*/
FROM Inventory
WHERE i.Converted = 0
AND i.CartID IS NULL
)
UPDATE i
SET i.Converted = 1,
i.CartID = q.CartID,
i.ReservedDate = GETDATE()
FROM cteInventory i
INNER JOIN cteProducts q ON q.ProductID = i.ProductID
WHERE i.rn <= q.QuantitySold;

Ranking customers by orders per customer and items per order

I'm trying to rank a query by not just one count, but by two.
I want to rank customers by the order items per orders.
WITH CTE AS
(
SELECT
o.CustomerId,
COUNT(DISTINCT o.OrderId) AS OrderCount,
COUNT(oi.OrderItemId) AS OrderItemCount
FROM
OrderItem oi
INNER JOIN
Order o ON o.OrderId = oi.OrderId
WHERE
o.CategoryId = 52 -- website sales
GROUP BY
o.CustomerId
)
SELECT
cust.Code,
cust.DisplayTitle,
CTE.OrderCount,
CTE.OrderItemCount,
--AVG(CTE.OrderItemCount/CTE.OrderCount) AS SumProduct ????
FROM
CTE
INNER JOIN
Customer cust ON cust.CustomerId = CTE.CustomerId
GROUP BY
cust.Code,
cust.DisplayTitle,
CTE.OrderCount,
CTE.OrderItemCount
ORDER BY
SumProduct DESC
I'm basically trying to implement the T-SQL equivalent of SUMPRODUCT() in Excel.
SELECT
o.CustomerId,
COUNT(DISTINCT o.OrderId) AS OrderCount,
COUNT(oi.OrderItemId) AS OrderItemCount,
COUNT(oi.OrderItemId) / COUNT(DISTINCT o.OrderId) avg
FROM OrderItem oi
INNER JOIN Order o ON o.OrderId = oi.OrderId
WHERE o.CategoryId = 52 -- website sales
GROUP BY o.CustomerId
order by COUNT(oi.OrderItemId) / COUNT(DISTINCT o.OrderId) desc
Just add in the join to customer

Where clause removing calculations from my query SQL Server

I am having a trying to calculate a result set for myself but I am facing a strange problem where the WHERE clause is creating problems for me.
The table are as follows:-
Customer
CustomerId CustomerName
2 Jayesh
5 Hasan
SaleInvoiceMaster
CustomerId TotalInvoiceAmount
1 50000
PurchaseInvoiceMaster
CustomerId TotalInvoiceAmount
1 10000
PaymentTransactions
CustomerId PaymentAmount
1 10000
The result set that is working is as follows:-
SELECT DISTINCT C.CustomerId AS CustomerId, C.Name AS CustomerName, ISNULL(SIM.TotalSale, 0), ISNULL(PIM.TotalPurchase, 0), ISNULL(PT.TotalPaid, 0), ISNULL(SIM.TotalSale - PIM.TotalPurchase - PT.TotalPaid, 0) AS AmountPending
FROM Customers AS C
LEFT OUTER JOIN (SELECT CustomerId, ISNULL(SUM(TotalInvoiceAmount),0) AS TotalSale FROM SaleInvoiceMaster GROUP BY CustomerId) AS SIM ON SIM.CustomerId = C.CustomerId
LEFT OUTER JOIN (SELECT CustomerId, ISNULL(SUM(TotalInvoiceAmount),0) AS TotalPurchase FROM PurchaseInvoiceMaster GROUP BY CustomerId) AS PIM ON PIM.CustomerId = C.CustomerId
LEFT OUTER JOIN (SELECT CustomerId, PaymentStatus, ISNULL(SUM(PaymentAmount),0) AS TotalPaid FROM PaymentTransactions AS P GROUP BY CustomerId, PaymentStatus) AS PT ON PT.CustomerId = C.CustomerId
The result for the same is as shown in below image
Result Set
When I add a where clause at the end f the third left outer join, the problem arises. The calculation stops and it shows zero. My updated query is as follows:-
SELECT DISTINCT C.CustomerId AS CustomerId, C.Name AS CustomerName, ISNULL(SIM.TotalSale, 0), ISNULL(PIM.TotalPurchase, 0), ISNULL(PT.TotalPaid, 0), ISNULL(SIM.TotalSale - PIM.TotalPurchase - PT.TotalPaid, 0) AS AmountPending
FROM Customers AS C
LEFT OUTER JOIN (SELECT CustomerId, ISNULL(SUM(TotalInvoiceAmount),0) AS TotalSale FROM SaleInvoiceMaster GROUP BY CustomerId) AS SIM ON SIM.CustomerId = C.CustomerId
LEFT OUTER JOIN (SELECT CustomerId, ISNULL(SUM(TotalInvoiceAmount),0) AS TotalPurchase FROM PurchaseInvoiceMaster GROUP BY CustomerId) AS PIM ON PIM.CustomerId = C.CustomerId
LEFT OUTER JOIN (SELECT CustomerId, ISNULL(SUM(PaymentAmount),0) AS TotalPaid FROM PaymentTransactions AS PT1 WHERE (PT1.PaymentStatus = 'Payment Made' OR PT1.PaymentStatus = 'Bad Debt') GROUP BY CustomerId) AS PT ON PT.CustomerId = C.CustomerId
The result set becomes:-
Result denied
IN your main select body you are doing this:
ISNULL(SIM.TotalSale - PIM.TotalPurchase - PT.TotalPaid, 0) AS AmountPending
This is returning 0 if ANY of those columns are NULL, you want this instead:
ISNULL(SIM.TotalSale, 0) - ISNULL(PIM.TotalPurchase, 0) - ISNULL(PT.TotalPaid, 0) AS AmountPending
Or for more current syntax, use COALESCE instead of ISNULL:
COALESCE(SIM.TotalSale, 0) - COALESCE(PIM.TotalPurchase, 0) - COALESCE(PT.TotalPaid, 0) AS AmountPending
You are have several confusions in the query. First, you don't need select distinct in the outermost query. Second, there is a difference between a subquery returning no matching rows and a NULL value in the subquery. Third, you are adding potentially NULL values together.
An improved version of the query:
SELECT C.CustomerId, C.Name AS CustomerName,
COALESCE(SIM.TotalSale, 0) as TotalSale,
COALESCE(PIM.TotalPurchase, 0) as TotalPurchase,
COALESCE(PT.TotalPaid, 0) as TotalPaid,
(COALESCE(SIM.TotalSale, 0) - COALESCE(PIM.TotalPurchase, 0)
- COALESCE(PT.TotalPaid, 0)
) as AmountPending
FROM Customers C LEFT OUTER JOIN
(SELECT CustomerId, SUM(TotalInvoiceAmount) AS TotalSale
FROM SaleInvoiceMaster
GROUP BY CustomerId
) SIM
ON SIM.CustomerId = C.CustomerId LEFT OUTER JOIN
(SELECT CustomerId, SUM(TotalInvoiceAmount) AS TotalPurchase
FROM PurchaseInvoiceMaster
GROUP BY CustomerId
) PIM
ON PIM.CustomerId = C.CustomerId LEFT OUTER JOIN
(SELECT CustomerId, SUM(PaymentAmount) AS TotalPaid
FROM PaymentTransactions PT1
WHERE PT1.PaymentStatus IN ('Payment Made', 'Bad Debt')
GROUP BY CustomerId
) PT
ON PT.CustomerId = C.CustomerId;
Removing the SELECT DISTINCT improves performance and makes the query more readable. Assuming CustomerId is unique in Customers, duplicates cannot be created.
Removing the ISNULL() in the subqueries makes the query more readable. This operation does nothing, because the NULLs need to be handled after the JOIN.
Replacing the OR with IN make the query more readable.

Error with sub query using ROW_NUMBER()

Good day,
In a WHILE statement I am running, one of the variable assignments I'm attempting is to use a sub query in conjunction with row_number() to pull out each specific ID from a column and sum a monetary value from another table, per each ID. I am working in MS SQL 2008.
Here is the query I have so far:
SET #value =(SELECT PaymentAmount FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY C.CategoryID ASC) AS RowNum,
SUM(O.PaymentAmount)
FROM Orders O
INNER JOIN OrderDetails OD ON O.OrderID = OD.OrderID
INNER JOIN Products_Joined PJ ON OD.ProductCode = PJ.ProductCode
INNER JOIN Categories_Products_Link CPL ON PJ.ProductID = CPL.ProductID
INNER JOIN Categories C ON CPL.CategoryID = C.CategoryID
GROUP BY C.CategoryID
) AS Num
WHERE RowNum = #counter)
The error I'm running into is in regards to no column was specified for column 2 of 'Num'.
Thoughts? Ideas? Thanks for your time!

How do you select multiple columns from multiple tables along with total cost?

I have four tables, but for this I only need three tables, and I want to display the customerid, orderid, productid, quantity and the total cost which isn't in any table but need to calculate? so far I have managed to get it to display total cost for one order with the order id, but I want it to display all the columns mentioned above
Order:
order_id(primary key)
customer_id( foreign key)
orderline:
orderid(fk) + productid(fk) (pk)
quantity
product:
productid(pk)
price
What I have done is
select orderid, sum(rowcost) as totalcost
from (select o.quantity, o.productid, o.orderid, os.customerid, p.price,
(o.quantity * p.price) as rowcost
from orderline o
inner join order os
on o.orderid = os.orderid
inner join product p
on p.productid = o.productid
where productid = 123)
group by orderid;
Now I want it to display all the orderids along with the productid, customerid, totalcost, orderid and quantity. The list should follow customerid order.
How would I do this?
when I add more variables in the select, it gives me errors. I have tried many ways, none of them worked.
do you mean you want something like this:
select o.quantity, o.productid, o.orderid, os.customerid, p.price,
(o.quantity * p.price) as rowcost,
sum(o.quantity * p.price) over (partition by os.customerid) as totalcost
from orderline o
inner join order os
on o.orderid = os.orderid
inner join product p
on p.productid = o.productid
where p.productid = 123
or this, to keep the sum correct if you wanted to filter afterwards on a product
select *
from (select o.quantity, o.productid, o.orderid, os.customerid, p.price,
(o.quantity * p.price) as rowcost,
sum(o.quantity * p.price) over (partition by os.customerid) as totalcost
from orderline o
inner join order os
on o.orderid = os.orderid
inner join product p
on p.productid = o.productid)
where productid = 123--or just remove this where clause
fiddle: http://sqlfiddle.com/#!4/3103d/1

Resources