I'm trying to build a cart system. So far, I can pull the user cart data easily with some join and even calculate the total price based on unit price and quantity user has in another column. However, I wanted to know if there is a way to actually compute the total amount owed directly from an SQL query. Allow me to showcase an example. Here are my tables:
products
product_id | product_name | product_price | stock
1 | Pasta | 10 | 50
2 | Bread | 2 | 100
cart
user_id | product_id | quantity
1 | 1 | 3
1 | 2 | 2
My Current Query
SELECT
`cart`.`product_id`,
`cart`.`user_id`,
`cart`.`quantity`,
`products`.`product_price` as `unit_price`,
`products`.`product_name`,
`cart`.`quantity` * `products`.`product_price` as `Total`
FROM `cart`
INNER JOIN `products` ON `products`.`product_id` = `cart`.`product_id`
WHERE `cart`.`u_id` = 1;
As you can see, the query above will work and return me all the products in the cart of a specific user and add the Total column to the result with the total price for each item.
Now, if I want to calculate the gross total, I have to read each row in my PHP code and do the total. Although I can do this, I was wondering if MySQL has a way of returning just the gross total directly through a single query.
Bonus question: Is a cart table structure like the one above good enough?
You can use "SUM" Operator in MYSQL to get the value from database directly.
Here is a documentation where you can get more info:
https://www.mysqltutorial.org/mysql-sum/
Regarding the code here is an example:
SELECT SUM(`cart`.`quantity` * `products`.`product_price`) as `CartTotal`
FROM `cart`
INNER JOIN `products` ON `products`.`product_id` = `cart`.`product_id`
WHERE `cart`.`u_id` = 1;
You can calculate total cart cost using SUM aggregation function.
SELECT SUM(`cart`.`quantity` * `products`.`product_price`) as `CartTotal`
FROM `cart`
INNER JOIN `products` ON `products`.`product_id` = `cart`.`product_id`
WHERE `cart`.`u_id` = 1;
Also you can use UNION construction for combine cart total result to main query like:
-- Get cart content for user
SELECT
`cart`.`product_id`,
`cart`.`user_id`,
`cart`.`quantity`,
`products`.`product_price` as `unit_price`,
`products`.`product_name`,
`cart`.`quantity` * `products`.`product_price` as `Total`
FROM `cart`
INNER JOIN `products` ON `products`.`product_id` = `cart`.`product_id`
WHERE `cart`.`u_id` = 1
UNION -- Union summary row to cart results
SELECT
NULL as `product_id`, -- set NULL for non aggregated field
`cart`.`user_id`,
COUNT(*), -- calculate items count in cart
NULL as `unit_price`, -- set NULL for non aggregated field
'Total' as `product_name`, -- set 'Total' string as product name
SUM(`cart`.`quantity` * `products`.`product_price`) as `CartTotal` -- calculate total cart cost
FROM `cart`
INNER JOIN `products` ON `products`.`product_id` = `cart`.`product_id`
WHERE `cart`.`u_id` = 1;
Related
i'm looking for solution to check if multiple rows from one table have match in other table. In my situation i need to check if items from orders are in storage. Currently I use php to check orders - script is taking open orders and foreach one by one to check storage. It's generating quite a lot of queries and it's not efficient at all and i'm looking for solution to do this via sql query.
Desired result should be:
OrderId | Date | Products
1002/02 | 2020/08/16 | 1x Ipod; 2x battery; 9x some item;
0333/4 | 2020/06/22 | 10x shelf
Storage products table
id | id_product | quantity
Orders
id | reference | id_status | created_at
Order Products
Id | id_order | quantity | id_storage_product
I've written some code to generate table visible above but result it's not even close to desired.
select('orders.id', orders.created_at','orders.reference', 'storage_products.id as storageProductId')
->join('order_products', 'orders.id', '=', 'order_products.id_order')
->join('storage_products', 'order_products.id_product', '=', 'storage_products.id_product')
->where('storage_products.quantity', '>=', 'order_products.quantity')
->whereIn('orders.id_status', array(1, 2)) //get new orders/ open
->where('order_products.id_storage_product', null)
->groupBy('orders.id');
Clean sql:
SELECT `orders`.`id`,
`orders`.`created_at`,
`orders`.`reference`,
`storage_products`.`id` AS `storageProductId`,
`order_products`.`id_order`
FROM `orders`
INNER JOIN `order_products`
ON `orders`.`id` =
`order_products`.`id_order`
INNER JOIN `storage_products`
ON `order_products`.`id_product` =
`storage_products`.`id_product`
WHERE `storage_products`.`quantity` >=
'order_products.quantity'
AND `orders`.`id_status` IN ( 1, 2 )
AND `order_products`.`id_storage_product` IS NULL
GROUP BY `orders`.`id`
ORDER BY `orders`.`id` ASC
So code should find open orders (id_status); where storage quantity is equal or greater than product in order; where id_storage_products is null (means product bought on website but it was not in storage when ordered).
Upper query result is wrong because it showed me partial match to storage - even without checking quantity (some products have 0 but still displayed).
For any help many thanks
EDIT: fiddle sample: https://www.db-fiddle.com/f/6jKvKXPYvsLeXgm3Qv1nHu/0
Your query contains the condition:
AND `order_products`.`id_storage_product` IS NULL
but in your sample data all values are 0.
So instead I use COALESCE() to cover both cases.
Also I removed the condition:
AND `orders`.`id_status` IN ( 1, 2 )
because the column id_status is not included in the definition of the table orders in your sample data.
This query works:
SELECT o.id,
o.reference,
o.created_at,
GROUP_CONCAT(op.quantity, 'x', op.id_product separator ' ;') products
FROM orders o
INNER JOIN order_products op ON o.id = op.id_order
INNER JOIN storage_products sp ON op.id_product = sp.id_product
WHERE sp.quantity >= op.quantity AND COALESCE(op.id_storage_product, 0) = 0
GROUP BY o.id, o.reference, o.created_at
ORDER BY o.id ASC
See the demo.
Results:
| id | reference | created_at | products |
| --- | --------- | ------------------- | ------------- |
| 2 | 345554/02 | 2020-08-22 00:00:00 | 3x188 ; 1x155 |
If you also join the table products (I assume there is such a table) you can get the names of the products instead of their ids.
I tried the following query on the db-fiddle link and this works.
SELECT
orders.reference, orders.created_at, order_products.id_product
FROM
storage_products
LEFT JOIN
order_products ON storage_products.id_product = order_products.id_product
LEFT JOIN
orders ON orders.id = order_products.id_order;
What I did in the query is calling all storage_products with the same id_product in order_products and proceed to call all orders in the called order_products.
I have two tables:
booking - records the order detail
id | booking_amount
-------------------
1 | 150
2 | 500
3 | 400
payment - records the payment for order
id | booking_id | amount
------------------------
1 | 1 | 100
2 | 1 | 50
2 | 2 | 100
I want to find all bookings where the payments are not complete. With the above data, we expect the answer to be 2,3, because the sum of payments for booking_id=1 matches the corresponding booking_amount in the booking_table.
To answer your question, you have 2 things you need to think about :
you want the total amount in your table payment by every booking row
you want to join your booking_amount table with payment.
Part 1 is quite simple:
SELECT sum(amount) as TotalP, booking_id FROM payment GROUP BY booking_id
Just a basic query with a simple aggregate function...
For part 2, we want to join booking_amount and payment; the basic JOIN would be:
SELECT * FROM booking b
LEFT JOIN payment p ON b.id = p.booking_id
We do a LEFT JOIN because we may have some booking who are not in the payment table. For those bookings, you will get NULL value. We will use a COALESCE to replace the NULL values by 0.
The final query is this:
SELECT b.id, COALESCE(TotalP, 0), b.booking_amount
FROM
booking b
LEFT JOIN
(SELECT sum(amount) as TotalP, booking_id FROM payment GROUP BY booking_id) as T
ON b.id = T.booking_id
WHERE COALESCE(TotalP, 0) < b.booking_amount
You need to use a outer join to combine your two tables and look for your conditions. Also, you will need to use SUM(..) function to get the sum of the amount for each id in the payment table.
Please try this:
select b.id from booking b
left outer join -- cant be inner join because we lose id:3 in that case.
(
select booking_id, SUM(amount) as Total
from payment group by booking_id
) p on b.id = p.booking_id
where b.booking_amount > Coalesce(Total,0) --Coalesce is required for such values coming NULL, like id:3, they will be assigned as 0.
I am currently working on an auction system, and the functionality is all complete. I now need to add a count to the user's profile which shows how many items the user is currently bidding on.
The system comprises of two key tables (extra tables feature in the system of course, but these are the only tables related to this issue):
item_sales:
+-----+------------------+------------+-------------+---------+
| id | selling_format | duration | list_date | buyer |
+-----+------------------+------------+-------------+---------+
item_sales_bids:
+-----+-------------+-----------+---------------+-----------+--------+
| id | sale_item | user_id | current_bid | max_bid | date |
+-----+-------------+-----------+---------------+-----------+--------+
item_sales_bids.date is a Unix timestamp of the bid time.
I can easily get a count of all bids a given user has made with the following query:
SELECT COUNT(DISTINCT(`item_sales_bids`.`user_id`)) AS `total`,
SUM((`sale`.`list_date` + (`sale`.`duration` * 86400)) - UNIX_TIMESTAMP()) AS `endTime`
FROM `item_sales_bids`
INNER JOIN `item_sales` `sale` ON `item_sales_bids`.`sale_item` = `sale`.`id`
WHERE `user_id` = 1
GROUP BY `sale_item`
HAVING `endTime` > 0
What I would like to do, is run a query similar to the above, but only include records where the specified user is the current highest bidder (i.e. the max ID entry for a given item's bid set has a user_id value = to our user).
Unfortunately, I'm at a loss on how I might achieve this.
I have set up an SQLFiddle to assist > http://sqlfiddle.com/#!2/b98e4/3
Do a subs query to get the latest bid for all items and then join that to item_sales_bids to only process the latest items.
Something like this:-
SELECT COUNT(DISTINCT(item_sales_bids.user_id)) AS total,
SUM((sale.list_date + (sale.duration * 86400)) - UNIX_TIMESTAMP()) AS endTime
FROM item_sales_bids
INNER JOIN item_sales sale ON item_sales_bids.sale_item = sale.id
INNER JOIN
(
SELECT sale_item, MAX(id) AS LatestBid
FROM item_sales_bids
GROUP BY sale_item
) Sub1
ON item_sales_bids.sale_item = Sub1.sale_item AND item_sales_bids.id = Sub1.LatestBid
WHERE user_id = 1
GROUP BY item_sales_bids.sale_item
HAVING endTime > 0
This should do the trick. Toggling between user 1 & 2 will show the desired behavior. In the sample data, user 1 returns no data (they are not the high bidder) and user 2 returns a single row, with a current high bid of 50.
SELECT COUNT(DISTINCT(`bids`.`user_id`)) AS `total`,
`highbids`.`sale_item`,
`highbids`.`maxBid`,
SUM((`sale`.`list_date` + (`sale`.`duration` * 86400)) - UNIX_TIMESTAMP()) AS `endTime`
FROM `item_sales_bids` `bids`
INNER JOIN `item_sales` `sale` ON `bids`.`sale_item` = `sale`.`id`
INNER JOIN (SELECT MAX(`current_bid`) AS `maxBid`, `sale_item`, `user_id`
FROM `item_sales_bids`
GROUP BY `sale_item`, `user_id`
ORDER BY `current_bid` DESC LIMIT 1,1)
AS `highbids` ON `bids`.`user_id` = `highbids`.`user_id`
WHERE `bids`.`user_id` = 2
GROUP BY `bids`.`sale_item`
HAVING `endTime` > 0
I have 3 models:
Product
Daily Log
Click
The tables look like:
Products
id
,
Daily Logs
id | product_id | price
,
Clicks
id | product_id | daily_log_id
For reporting, I would like to get all products that have accrued more than x cost (based on price per click).
In Ruby, the code might look like
products.select do | p |
cost = 0
p.daily_logs.each do | dlog |
cost += dlog.clicks.count * dlog.price
end
cost > x ? true : false
end
How might I write a similar query for mysql?
Thanks!
I think this is what you are looking for:
SELECT dl.product_id AS `product_id`, COUNT(c.id) AS `total_clicks`, SUM(dl.price) AS `total_price`
FROM daily_logs AS dl
INNER JOIN clicks AS c on dl.id = c.daily_log_id
WHERE `total_price` > ?
GROUP BY `product_id`
Note I didn't join the products table as it only has an id field, which is available in the daily_logs table as well. If the products table actually has more fields with information that you need, you could join that table as well and select the needed fields.
so i have a table with products
Product ID | Product Name
===========+===============
1 | Tissues
2 | Glass
I have a table with sales
Sale ID | Product ID | Quantity | Price
===========+============+==========+=============
1 | 1 | 1 | 55
2 | 2 | 1 | 60
and i have a table of purchases
Batch ID | Total Value | Quantity | Product ID
=========+=============+==========+==================
1 | 100 | 100 | 1
2 | 10 | 50 | 2
3 | 1 | 1 | 2
So im trying to calculate the profit based on average cost using the query
SELECT tblsale.product_id,
tblproduct.product_name,
SUM(tblsale.`quantity`) qty,
SUM(tblsale.`Price`*tblsale.`quantity`) sales,
(SELECT sum(total_value) / sum(quantity) VWAP
FROM tblpurchases
WHERE product_id = tblsale.product_id) average_price,
(average_price * qty) cost,
(sales-cost) profit
FROM tblsale, tblproduct
WHERE tblproduct.product_id = tblsale.`product_id`
GROUP by tblsale.`product_id`
But i can't seem to get it to work i get a 'average price' is an unknown column, how would I structure the query correctly
SQL doesn't support referencing a column alias in the same SELECT clause - that's why your average_price column is returning the 1054 error. You either have to do whatever operation you need in a subselect, derived table/inline view, or reuse the underlying logic where necessary. Here's an example of the reuse of logic:
SELECT prod.product_id,
prod.product_name,
SUM(s.quantity) qty,
SUM(s.Price * s.quantity) sales,
SUM(pur.total_value) / SUM(pur.quantity) average_price,
SUM(pur.total_value) / SUM(pur.quantity) * SUM(s.quantity) cost,
SUM(s.Price * s.quantity) - (SUM(pur.total_value) / SUM(pur.quantity) * SUM(s.quantity)) profit
FROM tblproduct prod
LEFT JOIN tblsale s ON prod.product_id = s.product_id
LEFT JOIN tblpurchases pur ON pur.product_id = prod.product_id
GROUP BY s.product_id
My query is using ANSI-92 JOIN syntax, which I recommend over the ANSI-89 syntax your query uses. See this question for more details.
How did you get to this query? It is completely off.. When writing a query, start small and then build it up. The query you have now is a complete mess and nowhere near to valid, there are random parenthesis' through out it.
To make a start, use indentation to make your query readable
SELECT p.product_id, p.product_name
, SUM(s.quantity) number_of_sales
, SUM(s.price) total_profit
, SUM(pu.quantity) purchase_quantity
, SUM(pu.value) purchase_value
, (SUM(pu.quantity) - SUM(s.quantity)) number_in_stock
, (SUM(s.price) - SUM(pu.value)) profit
, (SUM(pu.value) / SUM(pu.quantity)) avarage_purchase_price
FROM product p
LEFT JOIN sales s ON s.product_id = p.product_id
LEFT JOIN purchase pu ON pu.product_id = p.product_id
GROUP BY s.product_id, pu.product_id
"But i can't seem to get it to work i get a 'average price' is an unknown column, how would I structure the query correctly"
What is 'average price'? How would you like average price to be calculated? And the same for 'average cost'