mysql: Join product table with latest product prices - mysql

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

Related

Find out extra quantity available in a table

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

How to calculate inventory using mysql

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

select max, group by and display other column that's not in group by clause

To keep it short, I have found a tutorial online and followed to the point: http://www.tizag.com/mysqlTutorial/mysqlmax.php
SELECT type, MAX(price) FROM products GROUP BY type
My question is:
How do I echo which "clothing" is the most expensive (In this case "Blouse")?
UPDATE:
Sorry guys, my bad. I needed to make myself more clear. What I am looking for is a solution that shows each "name" where they are most expensive:
name type price
Clothing Blouse 34.97
Toy Playstation 89.95
Music Country Tunes 21.55
Try the following query:
Solution #1:
SELECT
products.name,
products.type,
products.price
FROM products
INNER JOIN
(
SELECT type,MAX(price) max_price
FROM products
GROUP BY type ) t
ON products.type = t.type
AND products.price = t.max_price;
Demo Here
Solution #2:
SELECT
products.name,
products.type,
products.price
FROM
products
WHERE (type, price) IN (
SELECT type, MAX(price) max_price
FROM products
GROUP BY type )
See Demo
EDIT:
Note: Both solutions might give you multiple products under same type if they share the same maximum price.
If you strictly want at most one item from each type then you need to group by again in the last line.
So for both solutions the last line would be:
GROUP BY products.type, products.price
See Demo of it
try this
select *
from product where
price=(select max(price)
from product where type='Clothing')
this will select the entire row in which the price is the max in clothing type
This will then order the item by descending price (starting with most expensive) and then display just the first item...
SELECT name, MAX(price)
FROM products
WHERE type = 'Clothing'
ORDER BY price DESC
LIMIT 1
You can find the most expensive (highest value) clothing by simply ordering on price and limit the number of records to 1.
SELECT name
FROM products
WHERE type = 'Clothing'
ORDER BY price DESC
LIMIT 1
edit:
As mentioned by Matt, if more than one product has the most expensive price and you want them all. You should include a subquery to fetch more than one.
SELECT name
FROM products
WHERE type = 'Clothing'
AND price = (SELECT MAX(price)
FROM products
WHERE type = 'Clothing')
With this query you can see all the relations , not just clothing
SELECT maxPricesPerType.type,maxPricesPerType.maxPrice,priceTypes.name
FROM
(
SELECT type, MAX(price) maxPrice FROM products GROUP BY type
) maxPricesPerType
INNER JOIN
(
SELECT type, price,name FROM products GROUP BY type,price
) priceTypes
ON priceTypes.type = maxPricesPerType.type
AND priceTypes.price = maxPricesPerType.maxPrice
This joins your first array with an array that has unique price-type combinations and displays the name of the max price per type you were asking.

How to get all order ID which not paid in SQL Server 2008?

I want to get all order id numbers for selected customer which not paid till now, my data show as following:
What I want is Write a SELECT statement that answers this question:
select orderID
from order
where customer id = #custID
and Total cashmovementValue
for current order id
is less than total (sold quantity * salePrice )
for current order id
How to do it?
Thanks.
You need to compare the sum of each order line with the sum of each payment per order. GROUP BY and a few sub-queries is what you need to get the job done.
Something like this should work:
SELECT
O.OrderID
FROM [Order] O
INNER JOIN (
-- Add up cost per order
SELECT
OrderID,
SUM(SoldQuantity * P.SalePrice) AS Total
FROM OrderLine
INNER JOIN Product P ON P.ProductID = OrderLine.ProductID
GROUP BY OrderID
) OL ON OL.OrderID = O.OrderID
LEFT JOIN (
-- Add up total amount paid per order
SELECT
OrderID,
SUM(CashMovementValue) AS Total
FROM CashMovement
GROUP BY OrderID
) C ON C.OrderID = O.OrderID
WHERE
O.CustomerID = #custID
AND ( C.OrderID IS NULL OR C.Total < OL.Total )
EDIT
I've just noticed you're not storing the sale price on each order line. I've updated my answer accordingly, but this is a very bad idea. What will happen to your old orders if the price of an item changes? It is okay (and actually best practice) to denormalise the data by storing the price at the time of sale on each order line.

MySQL subquery to get a list of IDs from one table to query a second table

I have two tables, one is products and one salesRecords
I am using this query:
SELECT
DAY(FROM_UNIXTIME(saleDate)) as day,
MONTH(FROM_UNIXTIME(saleDate)) as mnth,
YEAR(FROM_UNIXTIME(saleDate)) as yr,
COUNT(id) as invCount, SUM(quantity) as qty
FROM salesRecords WHERE itemNo IN
(SELECT GROUP_CONCAT(convert(id, CHAR(8))) as ids FROM products WHERE brand =100 GROUP by brand)
GROUP BY
mnth, yr
ORDER BY saleDate
The products table contains all I need to know about a product, and salesRecords contains details such as the saleDate, quantity sold, discounts given etc, so what I am trying to achieve here is a list of sales for all products with the brand id 100 by month and year from a Unix Time field called "saleDate"
It works, but is very slow.
Can anyone advise a faster way of doing this?
If I manually plug the list of IDs into the query it seems to work faster, so should I run two queries?
I have rewrote your query with a single inner join. Try it and come back with news:
SELECT
DAY(FROM_UNIXTIME(saleDate)) as day,
MONTH(FROM_UNIXTIME(saleDate)) as mnth,
YEAR(FROM_UNIXTIME(saleDate)) as yr,
COUNT(id) as invCount,
SUM(quantity) as qty
FROM salesRecords s inner join
products on s.itemNo = p.id
WHERE
p.brand =100
GROUP BY
`day`, mnth, yr
ORDER BY
saleDate
Also, to avoid where clause, you can put restrinction in on operation:
...
SUM(quantity) as qty
FROM salesRecords s inner join
products on p.brand =100 and s.itemNo = p.id
GROUP BY
...