Which JOIN type in multiple joins - mysql

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'

Related

MySQL Sum two columns (1 -> n)

I have a table 'rents' that has the field ID and a Foreign Key events_id and the field total_value, and i have another table 'payments' that has the field ID, a Foreign Key rents_id and the field payment.
Table events:
ID
Table rents:
ID,
total_value,
events_id
Table payments:
ID,
payment,
rents_id
There are many payments for the same ID of rents table, and there are many rents for the same ID of events table.
I need to create a query that shows the sum() for the field 'total_value' that has the same events ID in the rents table, and a sum() for the field 'payment' that has the same rents ID in the payments table.
I've managed to create this query that accomplish this task:
SELECT SUM(r.total_value) 'Total Value', (SELECT SUM(p.payment) FROM payments p INNER JOIN rents r ON p.rents_id = r.id WHERE r.events_id=8) 'Total Payment'
FROM rents r
WHERE r.events_id=8
;
I wonder if that's the only way to do that, or is there a better way to do it?
I would recommend a join - the idea is to compute intermediate payment sums in a subquery first, then join, and aggregate again in the outer query.
select sum(r.total_value) total_value, sum(p.payment) total_payment
from rents r
left join (
select rents_id, sum(payment) payment from payments group by rents_id
) p on p.rents_id = r.id
where r.events_id = 8
The left join avoid filtering out rents that have no payment at all.
You can easily change the query to generate the result for all event_ids at once:
select r.events_id, sum(r.total_value) total_value, sum(p.payment) total_payment
from rents r
left join (
select rents_id, sum(payment) payment from payments group by rents_id
) p on p.rents_id = r.id
group by r.events_id
A correlated subquery is probably the best approach (which I'll explain later). However, you don't need a join in the subquery, just a correlation clause:
SELECT SUM(r.total_value) as Total_Value,
(SELECT SUM(p.payment)
FROM payments p
WHERE p.rents_id = r.id
--------------^ correlation clause that "links" the subquery to the outer query
) as Total_Payment
FROM rents r
WHERE r.events_id = 8;
Why is this the best approach? First, with an index on payments(rents_id) and rests(events_id), this should be the fastest method.
Second, note that the filtering condition is only included once in the query. That makes it easy to update the query and less susceptible to error.
Third, this does not pre-aggregate the payments table. Pre-aggregating either requires repeating the filtering condition (as in your version of the query) or aggregating more data than necessary (which affects performance).
In addition, I would advise you to not use single quotes for column aliases. Only use single quotes for string and date constants.

Mysql - update column from another table data without reducing rows

I got stucked at the following scenario:
table Orders
order_id, order_date, order_customerid, order_state, order_city
table Customers
customer_id, customer_city, customer_state
I need to duplicate customer_city and customer_state into order_city and order_state, which is currently null. I tried using the following join:
select *
from orders o
inner join customers c
on o.customerid = c.id
and then using an update query. But it appears to be grouping orders by customer, so then it does not reflect the actual number of orders, causing some order_city and order_state to be left empty.
What can I do to update EVERY order inside the order table, knowing that there are many orders from the same customer?
You can use multi-table update syntax.
Something like this would work for you:
update orders
inner join customers on customers.customer_id = orders.order_customerid
set orders.order_state = customers.order_state,
orders.order_city = customers.order_city
where orders.order_state is null
or orders.order_city is null

mysql join Get unjoined values too

The below query is based on my payments table. which joins with daily orders table. I have list of bill numbers in my daily table. with this query I get only bill number for which the payment has been made. Would like to get the complete list of bill numbers from daily table and if payment not received then the received column to be empty.And order them with Bill-no Ascending
create or replace view view_pymts
As SELECT p.`order-id`, p.`order-item-code`,daily.Bill_no,daily.tally_sku,daily.`quantity-purchased` as quantity,daily.`item-price`+daily.`shipping-price`as inv_value,daily.rma_rcvd_amt as return_value, round((SUM(p.amount) + z.other),2) AS received
FROM payments p
INNER JOIN daily ON p.`order-item-code`= daily.`order-item-id`
JOIN (
SELECT `order-id`,
SUM(CASE WHEN `order-item-code` IS NULL
THEN amount
ELSE 0.0 END) / (COUNT(DISTINCT `order-item-code`)) AS other
FROM payments
GROUP BY `order-id`
) z
ON p.`order-id` = z.`order-id`
GROUP BY p.`order-id`, p.`order-item-code`
Replace INNER JOIN with LEFT JOIN. This will make all missing fields NULL in the table on the right of the join (the table after the join statement, rather than the one before, which is said to be on the left of the join).
EDIT: I didn't read the question carefully enough, and #minatverma is correct. You could either use a RIGHT JOIN in place of your INNER JOIN (right join doing the opposite of a left join, making null the fields missing in the table on the left), or switch the order of your tables and use a LEFT JOIN.

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

MySQL multiple row joining

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;