Group by returns wrong sum - php

Here are my 3 tables:
Products : (id, product_name)
Purchase Product : (id, product_id, purchase_price, quantity)
Sales Product : (id, product_id, sales_price, quantity, purhase_price)
I want to find the products which are on the purchase list as well as the sales list. If it's not on it the sales list it should return NULL for sales value as well as quantity.
Here same product has different different purchase price so I need to track which purchase product has been sold. But with the group by it's showing the wrong sum.
What could be a possible error of my query?
Here's my query:
$products = DB::table('products')
->join('purchase_products','purchase_products.product_id','products.id')
->leftjoin("sales_products",function($join){
$join
->on("sales_products.purchase_price","purchase_products.purchase_price")
->on("sales_products.product_id","purchase_products.product_id");
})
->select('products.product_name','purchase_products.purchase_price',DB::raw("SUM(purchase_products.quantity) as purchase_quantity"),'sales_products.sales_price',DB::raw("SUM(sales_products.quantity) as sales_quantity"))
->groupby('products.id','purchase_products.purchase_price')
->get();

When you join multiple tables, what is being summed is every combination of the joined tables. So if you have two sales records for a product, the sum of the distinct purchases will be doubled.
I can't tell you how to do it in laravel, but you can remove your sales join and use products.id in (select product_id from sales_products) instead to tell if a product has a sale, or instead of joining sales_products and purchase_products at the same time, join products and sales_products in a subquery that only returns distinct product ids.
Or if you really don't want to change your query structure, you can just change:
SUM(purchase_products.quantity)
to
SUM(purchase_products.quantity) / GREATEST(1, COUNT(sales_products.id))
By the way, I don't see where in your query you are setting sales to null if the product is not on the sales list.
Also, you probably want to group by purchase_products.id instead of purchase_products.purchase_price, in case a product has the same price twice.

Related

SQL query order by column asc/desc

I have an ecommerce site. On the site I do a query to get all of the products in a category based on price and this works:
SELECT DISTINCT pricing.product_id
FROM pricing INNER JOIN
product_categories
ON pricing.product_id=product_categories.product_id
WHERE product_categories.category_id='?'
ORDER BY pricing.base_price ASC
The issue I have is for products with multiple SKUs or variations. So in my pricing table there will be the same product id in as many rows as there are variations and each will have it's own base price.
My issue happens when one variation of one product is more expensive than the one that would generally be dearer.
What I want to know is if there is a way to order it so it is not by the lowest base price of all SKUs but the lowest base price for all SKUs with that product ID.
Use GROUP BY and MIN(base_price) to get the minimum price for each SKU, and order by that.
SELECT pricing.product_id
FROM pricing INNER JOIN
product_categories
ON pricing.product_id=product_categories.product_id
WHERE product_categories.category_id='?'
GROUP BY pricing.product_id
ORDER BY MIN(pricing.base_price) ASC

Combine multiple 'orders' from mysql table and display total price?

I'm working on a panel to show all orders from our e-commerce site and the way I have the orders set up is to have a row for each order referring to the customer id.
I'm working on showing all open orders on our back-end however the rows are showing multiple rows for the same order if (so if the orderID is 18, and the order has 2 items ordered there a 2 rows all the with the orderID of 18).
I'll include some screenshots below so you have an idea of what's happening.
This is my sql statement to show all open orders:
function GetAllOpenOrders (){
global $conn;
$sql = "SELECT * FROM customer_orders LEFT JOIN ordered_products ON ordered_products.custOrderID=customer_orders.custOrderID WHERE customer_orders.orderOpen=1";
return $result = $conn->query($sql);
}
So again, I want to combine the orders with multiple products and display the total price in the open orders screen.
You need an aggregate query to display the total. Something like:
SELECT sum(prod.totalPrice) as TotalPrice
FROM customer_orders
LEFT JOIN ordered_products ON ordered_products.custOrderID=customer_orders.custOrderID
WHERE customer_orders.orderOpen=1
group by customer_orders.custOrderID
I don't have your database to test that query but that should point you in the right direction.
You need a sub-query where you SUM the order totals and then aggregate. You also can't add the "product ID" to your query otherwise it will by definition break it down into rows for each product ordered.
For example
select
customer_order_id,
sum(product_total_price) as order_total
from
(select
customer_order_id,
product_id,
product_total_price
from table
group by 1,2,3)
group by 1
If you're looking to show the names of the products ordered in your 1 row, I suggest using a CONCAT function for the product names (so it's all in one field) and then you'll have the total in 1 row.

MySQL sum up numbers in a relational database

I'm making a web application to make customers order items for anything. For that I've made a MySQL database which has the following tables:
customers
orders
order-items
products
In the customers table is all the information about the person such as:
The customer ID, for the primary key and auto increment (id)
The first name (first_name)
The last name (last_name)
The email address (email_adress)
Information about the customer (customer_info)
Example:
In the orders table is all the specific information about it such as:
The order ID, for the primary key and auto increment (id)
Which customer ordered it, linked with id field from the customers table (customer_id)
Order information (order_info)
The location where the order needs to go to (location)
When the order was created (created)
Example:
In the order-items table are all the items which every customer ordered, this is being linked by the order-id from the previous table.
The ID, for primary key and auto increment, not used for any relation (id)
The order ID, used for which product is for which order. This is linked with the id field from the orders table (order_id)
The product ID, this is used for what product they ordered, this is linked with the id field from the products table. (product_id)
The amount of this product they ordered (quantity)
Example:
In the products table is all the information about the products:
The ID, for primary key and auto incrementing, This is linked with the product_id field from the order_items table (id)
The name of the product (name)
The description of the product (description)
The price of the product (price)
Example:
The problem
Bob ordered product_id 2, times 3. Which is the sandwich with beef with the price of 2.50, which we have to multiply by 3 because it has been ordered 3 times. Which is 7.50
Bob also ordered product_id 3, times 5. Which is the sandwich with chicken with the price of 3.00, which we have to multiply by 5 because it has been ordered 5 times. Which comes out on 15.00
Now I need to sum these up. Which is 15.00 + 7.50 = 22.50
The question
How do I get the product_id linked with the actual price of the product_id? which I can then multiply by the quantity.
And then sum up all those values with the same order_id
For the first order we get product 2 (Quantity 3) and product 3 (Quantity 5), which should add 2.503 + 3.005 = 22.50
You asked, "
How do i get the product_id linked with the actual price of the product_id? Which i can then multiply by the quantity... And then sum up all those values with the same order_id."
Like this:
SELECT OI.Order_ID, Sum(OI.Quantity * P.Price) Total_Price
FROM `order-items` OI
INNER JOIN Products P
on OI.Products_Id = P.ID
GROUP BY OI.Order_ID
Expected output for sample data in question:
ORDER_ID Total_price
1 22.50
This is why I asked about sample Output. I'm not sure what columns you were trying to return so I returned just the sum total for each order.
Now what this says
Return a row for each order showing the order ID and the total_price which is the sum of (quantity ordered * price all lines for that order.
This is accomplished by looking at the order-items table (in back tic's because I'm not sure if mySQL like's dashes (-) in names.). Joining these tables based on the product_Id between the ordered items table and the price table. and then group the results by the ordered item allowing the sum to aggregate all the rows for an order times the price of the item on that line of the order together.
You can build an intermediate table that has the totals for each order, then JOIN that with the orders table to get the customerID of that order, which you join with customers to get the customer name
SELECT C.FirstName, C.LastName, Totals.Order_ID, Totals.NetCost
FROM (SELECT OI.order_id, SUM(OI.quantity * P.price) as NetCost
FROM orderitems as OI INNER JOIN products as P ON OI.products_id = P.id
GROUP BY OI.order_id
) as Totals
INNER JOIN orders as O on O.ID = Totals.order_id
INNER JOIN customers as C on C.ID = O.customer_id
And the problem #xQbert is talking about is that if your customer places an order on Monday, you raise your price on Tuesday, and run this report on Wed, the report will show a total different from what the customer saw when he approved the order. A better design would be to store orderprice in orderitem, or at least the ordertotal in orders so subsequent price changes don't affect historic orders. The former is better in case a customer wants a refund on one item.
EDIT: I mean the problem #xQbert mentions in his comment, his answer came in ahead of mine but I missed it on the refresh
EDIT 2: More explanation, as requested by #Bas
It's easiest to think of this query from the inside and work outward, so let me start with getting the prices of items.
I want to link an item in orderitem with it's price, so JOIN orderitem (I give it an alias of OI) to products (I give an alias of P) on the product ID. Product ID is called products_id in OrderItem, but just ID in products.
Once I have that, I can multiply the price from Products by the quantity in OrderItems to get a cost for just that item
Because I want the total of everything for each order, I use a GROUP BY clause on this sub query, with the thing I'm grouping by being order_id. With a GROUP BY, you usually take the field or fields you are grouping by, and some "aggregate" fields where you take the sum, or max, or average, or whatever of them, and it's the sum or whatever for all rows that are in the same group.
So at this point we have defined the query inside the parenthesis. I named that Totals, and it acts just like a real table, but it goes away when your query is over.
The next step is to combine this "totals" table with your orders table so we can look up the customer ID, and then combine it with Customers so we can look up the customer name. I used aliases for Customer and Orders (C and O respectively), but they are not necessary. In fact, this could be rewritten most if not all of the aliases. I think it makes it more readable, but if they confuse you, you could use the following instead, it's the same. I think Totals is necessary, but maybe not even that.
SELECT FirstName, LastName, Totals.Order_ID, Totals.NetCost
FROM (SELECT order_id, SUM(quantity * price) as NetCost
FROM orderitems INNER JOIN products ON products_id = products.id
GROUP BY order_id
) as Totals
INNER JOIN orders on orders.ID = Totals.order_id
INNER JOIN customers on customers.ID = orders.customer_id
Hopefully this is clear, but if not, leave more comments and I'll further explain tonight or tomorrow.

mysql command to display table tuples according to the date

I have two tables which displays the contents of the tables according to the table,
I have two table product and sales
pcode is the primary key for product and foreign key to sales.
I need to display sales according to the particular days sale.
My Query is :
$result = mysql_query("SELECT product.pcode,pname,brand,price,oldbal,receipt,total,current,sales.date FROM product,sales WHERE sales.date='$date' AND product.pcode=sales.pcode");
this displays only product which was sold on that particular day, I need to display all product from product table and if they are sold they should display the data or null if its not.
How can i do this ?
What you need is known as LEFT JOIN.
SELECT product.pcode,pname,brand,price,oldbal,receipt,total,current,sales.date
FROM product LEFT JOIN sales
ON product.pcode=sales.pcode
WHERE sales.date=$date
You can use LEFT JOIN
SELECT product.pcode,pname,brand,price,oldbal,receipt,total,current,sales.date FROM product LEFT JOIN sales ON product.pcode = sales.pcode WHERE sales.date='$date';

mySql subquery to sum the many records of one item of a select?

I have a table called product and a table called sold.
I'm trying to figure out if there is a way to use one query to find all products and then for each product fond return in the result set a sum of sold.amount as total.
It's a very basic SQL join operation:
select product.id, sum(sold.amount)
from product
left join sold ON product.id = sold.product_id
group by product.id
if you want to list only products which have at least one sale, then change 'left join' to 'inner join'.

Categories