MySQL multiple row joining - mysql

I have an issue with joining of tables that I have not managed to solve. Somehow I have the impression that it is more simple than I think. Anyhow:
I have three tables:
orders
orderlines
payments
In "orders" every line corresponds to one order made by a customer. Every order has an order_id which is the primary key for that table. In "orderlines" I keep the content of the order, that is references to the products and services on the order. One order can, and typically has, many orderlines. Finally, in payments I store one row for every transaction made to pay for an order.
One order ideally never has more than one corresponding row in payments. But since customers are customers it is not unusual that someone pays the same invoice twice, hinting that the payments table can have two or more payments for one order.
Therefore it would be useful to create a query that joins all three tables in a relevant way, but I have not managed to do so. For instance:
SELECT orders.order_id, SUM(orderlines.amount), SUM(payments.amount)
FROM orders
LEFT JOIN orderlines
ON orders.order_id = orderlines.order_id
LEFT JOIN payments
ON orders.order_id = payments.order_id
GROUP BY orders.order_id
The purpose of this join is to find out if the SUM of the products on the order equals the SUM in payments. The problem here is that the two tables payments and orderlines "distract" each other by both causing multiple rows while joining.
Is there a simple solution to this problem?

Maybe I'm overcomplicating things, but using both tables and producing the sum would always lead too wrong results, i.e. one order has 10 orderline rows and 1 payment rows => the payment amount is going to be added 10 times. I guess you have to use subselects like this below (you didn't use anything from your table "orders" but the id, so I left it out, because all orders have orderlines):
SELECT t1.order_id, t1.OrderAmount, t2.PaymentAmount
FROM (SELECT SUM(amount) AS OrderAmount, order_id
FROM orderlines
GROUP BY order_id) AS t1
LEFT JOIN (SELECT SUM(amount) AS PaymentAmount, order_id
FROM payments
GROUP BY order_id) AS t2
ON t1.order_id=t2.order_id

I think what you want to do is get the sum of all the items, and the sum of all the payments, and then link them together. A sub-select is able to do this.
Something like: (ps I have no database on hand so it might not be valid sql)
SELECT * FROM orders
LEFT JOIN (SELECT order_id, SUM(amount) FROM orderlines GROUP BY order_id) AS ordersums
ON orders.order_id = ordersums.order_id
LEFT JOIN (SELECT order_id, SUM(amount) FROM payments GROUP BY order_id) AS paymentsums
ON orders.order_id = paymentsums.order_id;

Related

Adding column values together to form 1 result column

I have an online sales system I am developing and I’m working on the billing payment system.
I have the order total $ amount recorded on the database table with the order itself.
Example:
SELECT total FROM Orders WHERE id = '1'
Then, I’ve got another table that includes an individual record for each financial transaction (check, cc, etc.)
Example:
SELECT payment_amount FROM Payments WHERE order_id = '1'
What I would like to do is combine these two together when doing some reporting of which orders have not been paid in full and retrieve the balance of each order. I’d like to do this with a single query if possible...
This was what I tried...
SELECT o.id as order_id, o.total, (SELECT p.payment_amount FROM Payments as p WHERE o.order_id = o.id) as amount_paid_plus FROM Orders as o
This works great if there is only 1 entry in payments...but if there are two, I get this error
Subquery returns more than 1 row
I want the payments table results to be added together into one lump sum amount and returned as one variable in the query
I also need the query to still return info even if there are no payments in the payments table. It will just display amount paid as 0.
You can just change p.payment_amount to SUM(p.payment_amount) in your subquery. Note that you have an error in the subquery, it should probably be
(SELECT SUM(p.payment_amount) FROM Payments as p WHERE p.order_id = o.id)
Note change from o.order_id to p.order_id.
Try this query :
SELECT o.id, o.total, SUM(p.payment_amount) FROM Orders o, Payments p where p.order_id = o.id

count rows in one table based on the id's of the other table

I have two tables:
Orders
Orders product
I currently have the query that counts the amounts of orders:
SELECT COUNT(*) as total FROM orders WHERE DATE(`created_on`) = CURDATE()
This gives me the amount of orders from today
Now I want to change this query, so insted of that I count the orders I count the amount of products that they ordered.
The order_products table is like this:
-id
-order_id
-product_name
-product_weight
etc etc
I am only interested in the order_id because that links this table to the orders table. For every product that is ordered, 1 row is added in this table linked to the order with the order_id.
Is it possible to have one single query to select the count of that, or is that not possible? I think I have to use a LEFT JOIN for this operation, but I cannot seem to find it.
You just need to create inner join between those table and applying the relation throw foreign key then you need to add group by condition on what you are looking for
SELECT COUNT(*) as total FROM orders o inner join order_product op on(op.order_id=o.order_id) WHERE DATE(`created_on`) = CURDATE() group by op.id

SQL Join, right ? left ? inner?

working with mySql I would like to list all purchases that customers made on a specific cathegory of products.
So, I had 3 tables: customers (idCustomer, Name) , cathegories (idCategory, CategoryName) and orders (idOrder, idCustomer, idCathegory, Qty, Price)
But I want a listing with ALL of the customers.
Not only the one who bought that specific idCategory
I thought something like:
select sum(Orders.Qty), Customers.Name
from Orders
right join Customers on Orders.idCustomer = Customer.idCustomer
where Orders.idCategory = 'Notebooks'
group by Orders.idCategory
but this statement only lists the records for customers who exists in Orders table.
And I want all of them ( the one who didnt buy, with qty =0 )
thanks in advance
Most people find left join easier to follow than right join. The logic for left join is to keep all rows in the first table, plus additional information from the remaining tables. So, if you want all customers, then that should be the first table.
You will then have a condition on the second table. Conditions on all but the first table should be in the on clause rather than a where. The reason is simple: when there is no match, then the value will be NULL and the where condition will fail.
So, try something like this:
select sum(o.Qty) as sumqty, c.Name
from Customers c left join
Orders o
on o.idCustomer = c.idCustomer and
o.idCategory = 'Notebooks'
group by c.Name;
Finally, the group by should have a relationship to the select clause.
Try this query
select sum(Orders.Qty), Customers.Name
from Customers
right join Orders on Customer.idCustomer = Orders.idCustomer and Orders.idCategory = 'Notebooks'
group by Customers.Name

Which JOIN type in multiple joins

I have 4 tables that I want to be joined.
Customers
Traffic
Average
Live
I want to insert joined data of these tables to "Details" table.
The relationship between the tables is here:
each of Traffic, Average and Live tables have a "cid" that is the primary key of "Customers" table:
Traffic.cid = Customers.id
Average.cid = Customers.id
Live.cid = Customers.id
The query that I wrote is here:
INSERT INTO Details
(
cid, Customer_Name, Router_Name,
Traffic_Received,
Average_Received,
Live_Received,
date
)
(
SELECT Customers.id AS cid, Customers.name AS Customer_Name, Traffic.Router_Name,
Traffic.Received,
Average.Received,
Live.Received,
Traffic.date
FROM Customers
INNER JOIN Traffic ON Customers.id=Traffic.cid
INNER JOIN Average ON Customers.id=Average.cid
INNER JOIN Live ON Customers.id=Live.cid
WHERE Traffic.date='2015-06-08'
)
But the result will have duplicated rows. I changed the JOIN to both LEFT JOIN, and RIGHT JOIN. but the result does not changed.
What should I do to not have duplicated rows in Details table?
With the LEFT JOIN, you will be joining to the table (e.g. Traffic) even when there is not a record that corresponds to the Customers.id, in which case, you will get the null value for the columns from this table where there is no matching record.
With the RIGHT JOIN, you will get every record from the joined table, even when there is not a corresponding record in Customers.
However, the type of JOIN is not the problem here. If you are getting duplicate records in your results, then this means that is more than one matching record in the tables you are joining to. For example, there may be more than one record in Traffic with the same cid. Use SELECT DISTINCT to remove duplicates, or if you are interested in an aggregate of those duplicates, use an aggregate function, such as count() or sum() and a GROUP BY clause, e.g. GROUP BY Traffic.cid.
If you still have duplicates, then check to make sure that they really are duplicates - I'd suggest that one or more columns is actually different.
Can you please try this
INSERT INTO Details
(
cid, Customer_Name, Router_Name,
Traffic_Received,
Average_Received,
Live_Received,
date
)
(
SELECT Customers.id AS cid,
Customers.name AS Customer_Name,
Traffic.Router_Name,
Traffic.Received,
Average.Received,
Live.Received,
Traffic.date
FROM Customers
INNER JOIN Traffic ON Customers.id=Traffic.cid
INNER JOIN Average ON Customers.id=Average.cid
INNER JOIN Live ON Customers.id=Live.cid
WHERE Traffic.date='2015-06-08'
GROUP BY
cid,
Customer_Name,
Traffic.Router_Name,
Traffic.Received,
Average.Received,
Live.Received,
Traffic.date
)
SELECT Customers.id AS cid, Customers.name AS Customer_Name, Traffic.Router_Name,
Traffic.Received,
Average.Received,
Live.Received,
Traffic.date
FROM Customers
LEFT JOIN Traffic ON Customers.id=Traffic.cid
LEFT JOIN Average ON Traffic.cid=Average.cid
LEFT JOIN Live ON Average.cid=Live.cid
WHERE Traffic.date='2015-06-08'

MYSQL Count group by rows ignoring effect of JOIN and SUM fields on Joined tables

I have 3 tables:
Orders
- id
- customer_id
Details
- id
- order_id
- product_id
- ordered_qty
Parcels
- id
- detail_id
- batch_code
- picked_qty
Orders have multiple Details rows, a detail row per product.
A detail row has multiple parcels, as 10'000 ordered qty may come from 6 different batches, so goods from batches are packed and shipped separately. The picked quantity put in each parcel for a detail row should then be the same as the ordered_qty.
... hope that makes sense.
Im struggling to write a query to provide summary information of all of this.
I need to Group By customer_id to provide a row of data per customer.
That row should contain
Their total number of orders
Their total ordered_qty of goods across all orders
Their total picked_qty of goods across all orders
I can get the first one with:
SELECT customer_id, COUNT(*) as number_of_orders
FROM Orders
GROUP BY Orders.customer_id
But when I LEFT JOIN the other two tables and add the
SELECT ..... SUM(Details.ordered_qty) AS total_qty_ordered,
SUM(Parcels.picked_qty) AS total_qty_picked
.. then I get results that dont seem to add up for the quantities, and the COUNT(*) seems to include the additional lines from the JOIN which obviously then isn't giving me the number of Orders anymore.
Not sure what to try next.
===== EDIT =======
Here's the query I tried:
SELECT
customer_id,
COUNT(*) as number_of_orders,
SUM(Details.ordered_qty) AS total_qty_ordered,
SUM(Parcels.picked_qty) AS total_qty_picked
FROM Orders
LEFT JOIN Details ON Details.order_id=Order.id
LEFT JOIN Parcels ON Parcels.detail_id=Detail.id
GROUP BY Orders.customer_id
try COUNT(distinct Orders.order_id) as number_of_orders,
as in
SELECT
customer_id,
COUNT(distinct Orders.order_id) as number_of_orders,
SUM(Details.ordered_qty) AS total_qty_ordered,
(select SUM(Parcels.picked_qty)
FROM Parcels WHERE Parcels.detail_id=Detail.id ) AS total_qty_picked
FROM Orders
LEFT JOIN Details ON Details.order_id=Order.id
GROUP BY Orders.customer_id
EDIT: added an other select with subselect
Is there any particular reason you feel the need to combine all these in one query? Simplify by breaking it up in to separate queries, and if you want a single call to get the results, put the queries in a stored procedure, using temp tables.