I am looking for a way to calculate stock quantity from two tables.
My table records are as follows:
Below is my product table
I have two table first is "stockinward" for purchase the stock and another is stockoutward for sale out the stock.
Below is the screenshot for stockinward table
and here is the screenshot for stockoutward
Below is my query to calculate the stock
SELECT
p.Id,
p.Name,
p.UnitPrice,
((SELECT
IFNULL(SUM(Quantity), 0)
FROM
stockinward
WHERE
ProductId = p.Id) - (SELECT
IFNULL(SUM(Quantity), 0)
FROM
stockoutward
WHERE
ProductId = p.Id)) AS Quantity
FROM
product p;
But the issue is in above query, when i have more then 1000 products it takes more then 8 second, so is there any other way in which i get the same result in 1 or 2 seconds?
Thanks in advance :)
You could also use joins instead of subqueries
SELECT
p.Id,
p.Name,
p.UnitPrice,
IFNULL(qin.Quantity, 0) - IFNULL(qout.Quantity, 0) AS Quantity
FROM product
LEFT JOIN (
SELECT ProductId, SUM(Quantity) AS Quantity
FROM stockinward
GROUP BY ProductId
) qin ON p.Id = qin.ProductId
LEFT JOIN (
SELECT ProductId, SUM(Quantity) AS Quantity
FROM stockoutward
GROUP BY ProductId
) qout ON p.Id = qout.ProductId
Related
I have two tables, let's say OrderPlaced and OrderDelivered.
The OrderPlaced table looks like this:
In a single order we can have multiple products(which is defined by sku in the table) and each product can have multiple quantity.
The OrderDelivered table looks like this:
So technically 3 products have not been delivered. Orderid 1000 - product S101, Orderid 1001 - product S102(as 3 quantity required, but 2 delivered) and Orderid 1002 - product S100.
I am trying to write a SQL query that can give me the OrderId and sku those have not been delivered. For now I have written something like
select OrderPlaced.orderid,OrderPlaced.sku
from OrderPlaced
left join OrderDelivered
on OrderPlaced.Orderid = OrderDelivered.orderid and OrderPlaced.sku = OrderDelivered.sku
where OrderDelivered.sku is NULL;
This is giving me Orderid 1000 - product S101 and Orderid 1002 - product S100, but Orderid 1001 - product S102 is missing. I understand I have to do a check on qty as well, but couldn't think how to do that. I would really appreciate it if someone can help me with that part.
Add up the deliveries per order and sku and then outer join the delivered quantities to the order table so you can compare the quantities.
select
p.orderid,
p.sku,
p.qty as ordered,
coalesce(d.sum_qty, 0) as delivered
from orderplaced p
left join
(
select orderid, sku, sum(qty) as sum_qty
from orderdelivered
group by orderid, sku
) d on d.orderid = p.orderid and d.sku = p.sku
where p.qty > coalesce(d.sum_qty, 0)
order by p.orderid, p.sku;
Your query works for any items that have not been delivered at all, this is your WHERE OrderDelivered.sku IS NULL. But you can also have a scenario in which fewer items are delivered than ordered, and importantly, you can have multiple records related to your deliveries even if they refer to the same order and sku (two rows with 1 qty each).
In this case you will need to sum up all the deliveries per placed order id, sku and quantity (GROUP BY clause in the query below) check if that sum (or 0 if nothing is found) differs from the placed order (HAVING clause). You could use such a query:
SELECT OrderPlaced.orderid, OrderPlaced.sku,
OrderPlaced.qty - COALESCE(SUM(OrderDelivered.qty), 0) AS qty_missing,
CASE
WHEN SUM(OrderDelivered.qty) IS NULL
THEN 'Yes'
ELSE 'No'
END AS is_missing_completely
FROM OrderPlaced
LEFT
JOIN OrderDelivered
ON OrderPlaced.Orderid = OrderDelivered.orderid
AND OrderPlaced.sku = OrderDelivered.sku
GROUP BY OrderPlaced.orderid, OrderPlaced.sku, OrderPlaced.qty
HAVING OrderPlaced.qty != COALESCE(SUM(OrderDelivered.qty), 0)
Here's a live demo on dbfiddle
I would create two aggregated representations of your ordered and delivered products, and then outer join them to get the differences. If you are using MySql 8 you can represent these as a CTE, otherwise just use two equivalent sub-queries
with op as (
select OrderId, Sku, Sum(qty) Qty
from OrderPlaced
group by OrderId, Sku
), od as (
select OrderId, Sku, Sum(qty) Qty
from OrderDelivered
group by OrderId, Sku
)
select op.OrderId, op.Sku, op.Qty - Coalesce(od.qty,0) notDelivered
from op
left join od on od.orderid = op.orderid and od.sku = op.sku
where op.Qty - Coalesce(od.qty,0)>0;
Example DB<>Fiddle
I have the folowing tree tables one is the stock_items with all the items in it. The stock_in has the stock movements in to the stock and stock_out has the out movements:
and I want to get such a query result:
could some one help me to make this query?
You want to select the stock items and join the in totals and out totals. Aggregate to get the totals. Outer join to get items with and without transactions. Use COALESCE to replace nulls with zeros.
select
s.barcode, s.item_name,
coalesce(si.total, 0) as amount_in,
coalesce(so.total, 0) as amount_out,
coalesce(si.total, 0) - coalesce(so.total, 0) as balance,
s.unit
from stock_items s
left join
(
select barcode, sum(amount) as total
from stock_in
group by barcode
) si on si.barcode = s.barcode
left join
(
select barcode, sum(amount) as total
from stock_out
group by barcode
) so on so.barcode = s.barcode
order by s.barcode;
You can join tables using the barcode column?
select * from stock_items
join stock_in on stock_items.barcode = stock_in.barcode
join stock_out on stock_items.barcode = stock_out.barcode
Just replace the * with a list of desired column names
Unleash the power of subqueries and then use subtraction to compute the total stock level.
Basically the strategy here is to:
Run one query to sum up the total stock in
Run another query to sum up the total stock out
Run a final query subtracting the summed values.
select *, (stock_in - stock_out) as total from (select
product_id pid,
product_name,
product_unit,
(select sum(stock_in) from stock_in where product_id=pid) as stock_in,
(select sum(stock_out) from stock_out where product_id=pid) as stock_out
from products) summed
SQL fiddle here: https://www.db-fiddle.com/f/v95qsALSfnm66HoQb6PqJ6/0
I have a product-table and a table containing all parts of a product and the quantity.
Structure of products table:
id
name
Structure of parts table:
id
productsId
partsId
quantity
Now I want to get all products and for every product the total quantity parts and quantity of different parts . The current solution is this query:
SELECT
products.*,
(
SELECT count(quantity)
FROM product_has_part
WHERE product_has_part.productsId = products.id
) AS partsQty,
(
SELECT sum(quantity)
FROM product_has_part
WHERE product_has_part.productsId = products.id
) AS sumQty
FROM products
Now I have to subselects across the same table. So I think there must be a better way to create this query?
use join
Select *,count(quantity),sum(quantity)
From products p join
product_has_part pp on pp.productsId = p.id
FROM products
or Group by use for count or sum for each id
Select *,count(quantity),sum(quantity)
From products p join
product_has_part pp on pp.productsId = p.id
FROM products
Group by p.id
p.f1, p.f2, p.f3 mean each field of p.
select p.f1, p.f2, p.f3 ... count(php.quantity), sum(php.quantity)
from products p
join product_has_part php
on p.productsId = php.productsId
group by p.f1, p.f2, p.f3 ..
I have a table productPrice which contains more than one price for several products:
productPrice:
id,
unixTime,
productId,
price
I query the latest product prices and an average of all prices for this product in the last 24 hours:
SELECT
(SELECT AVG(price) FROM productPrice WHERE productId =19 AND unixTime >= (unix_timestamp(NOW())-86400)) as avg,
price,
unixTime
FROM productPrice
WHERE productId =19
ORDER BY unixTime DESC
LIMIT 1
This returns the latest price, unixTime and the average price in a reasonable time (in my opinion - there may be a better way to do this).
I have another table products, this is where I get the productId from:
products:
id (=productId in productPrice),
name,
url
I would like to select * from products and use the productId to join the result with the latest price and the average price, to get a result for all products like:
id,name,url,unixTime,price,avg
I read many similar questions here, but none seemed to work for me.
Is there a good way to do this, or should I select the product first and do a single select for each productId after?
Thank you in advance for any help!
EDIT: Included unixTime in the result to get more than one column from productPrice.
You can accomplish what you want with correlated subqueries:
select p.*,
(select avg(price)
from productPrice pp
where pp.productId = p.productid and
unixTime > (unix_timestamp(NOW()) - 86400)
) as avgprice,
(select price
from productPrice pp
where pp.productId = p.productid
order by unixTime desc
limit 1
) as mostrecentprice
from products p;
For performance, you want an index on productPrice(productid, unixtime, price).
I couldn't test here, but I believe that this might work:
SELECT p.id, p.name, p.url, pp.price, (SELECT AVG(pr.price) FROM productPrice pr WHERE pr.productId = p.id AND pr.unixTime >= (unix_timestamp(NOW()) - 86400)) avg,
FROM productPrice pp INNER JOIN products p ON pp.productId = p.id
WHERE pp.productId = 19
ORDER BY pp.unixTime DESC
I have a situation where a LEFT JOIN is not going far enough in what I need to accomplish. I have a product table (Products with columns ItemID, ProductName, Price) and an order table (Orders with columns OrderNumber, ItemID, Quantity).
I need the query to return all of the products from the Products table that are not currently a part of a specific order (for example, list all products that are not a part of OrderNumber 52).
My current query lists all of the products but excludes the products that are a part of ANY OrderNumber.
$query = "SELECT Products.ItemID, Products.ProductName
FROM Products
LEFT JOIN Orders
ON Orders.ItemID = Products.ItemID
WHERE Orders.ItemID IS NULL
ORDER BY Products.ProductName";
You can use a anti-join for this purpose, like so:
SELECT ItemID, ProductName
FROM Products
WHERE ItemID NOT IN (
SELECT ItemID
FROM Orders
WHERE OrderID = X
)
ORDER BY ProductName
SELECT Products.ItemID, Products.ProductName
FROM Products
WHERE Products.ItemID not in (select Orders.ItemID from Orders where Orders.OrderNumber = xxx)
ORDER BY Products.ProductName
What you need can be easily accomplished just by adding the order number in the join condition:
SELECT Products.ItemID, Products.ProductName
FROM Products
LEFT JOIN Orders
ON (Orders.ItemID = Products.ItemID AND OrderNumber = 52)
WHERE Orders.ItemID IS NULL
ORDER BY Products.ProductName