SQL Join, right ? left ? inner? - mysql

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

Related

SQL average and Join

I'm trying to merge these two statements into one query to get the a list of product names(or ids) against the average of their TTFF data, and I'm stuck.
select AVG(TTFF) from TTFFdata group by product_id
select product.product_name, count(*) from product join TTFFdata on product.product_id = TTFFdata.product_id
I've looked into using a temporary table (CREATE TEMPORARY TABLE IF NOT EXISTS averages AS (select AVG(TTFF) from TTFFdata group by product_id)) but couldn't get that to work with a join.
Anyone able to help me please?
You need to understand the components. Your second query is missing a group by. This would seem to be what you want:
select p.product_name, count(t.product_id), avg(t.TTFF)
from product p left join
TTFFdata t
on p.product_id = t.product_id
group by p.product_name
It is better to do group by on product_id, product_name for two reasons. One is, you can select product id along with product name. Second reason is, If the product name is not unique then it may give wrong results(this may be a rare scenario like product name is same but it differs based on other columns like version or model). The below is the final query.
select Product.product_id,
product_name,
AVG(TTFF) as Avg_TTFF
from Product
inner join
TTFFdata
on Product.product_id = TTFFdata.product_id
group by Product.product_id,Product.product_name
TTFFdata:
product:
Output:

Joining two tables using subquery in mysql

Im trying to write a SELECT statement with a subquery that determines the customer_id and the last_name of the customer
whose credit card expires soonest as shown in my orders table
and for this i want to use a WHERE clause but im stuck since the result only displays one customer...
here is what i have so far. if someone could guide me in the right path..
SELECT customer_id, last_name
FROM customers
WHERE NOT EXISTS
(SELECT card_expires
FROM orders
WHERE customers.customer_id = orders.customer_id
);
While I am not clear on your exact filtering or ordering requirements, I think you may want to start with the following.
SELECT customer_id, last_name, card_expires
FROM customers
left join (
SELECT card_expires
FROM orders
) o on (customers.customer_id = orders.customer_id)
Using this method you can see a full list of all customers and card_expires, and THEN you can start to filter the list down to what you want. You might build up the subquery on orders to select a specific subset of orders. Again I am not clear on what you are trying to do exactly. But by selecting all of the information first you will be able to see all of the information and be able to decide what to filter out.
You may decide you would ALSO like to see more columns from the order table to help you decide which orders to include / exclude.
If you wanted to only see a list of customers that do not have a card_expires entry
SELECT customer_id, last_name, card_expires
FROM customers
left join (
SELECT card_expires
FROM orders
) o on (customers.customer_id = orders.customer_id)
where o.card_expires is null
If you want to see only customers WITHOUT a card that expires in the future.
SELECT customer_id, last_name, card_expires
FROM customers
left join (
SELECT card_expires
FROM orders
where card_expires > now()
) o on (customers.customer_id = orders.customer_id)
where o.card_expires is null
When crafting an SQL statement, something that can make it easier to do is FIRST create an SQL statement which returns ALL of the columns you will use to filter the list by, THEN apply the filter. This will allow you to more easily add and remove criteria until you have it right.
I like to split each criteria on to its own separate line so that I can easily comment lines out with two dashes. EX:
SELECT customer_id, last_name, card_expires
FROM customers
left join (
SELECT card_expires
FROM orders
where card_expires > now()
) o on (customers.customer_id = orders.customer_id)
where 0=0
and o.card_expires is null
-- and last_name = 'Smith'
Use LEFT JOIN/INNER JOIN, i assume you want ORDER BY ASC, since you want the soonest
SELECT customer_id, last_name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
ORDER BY card_expires ASC;

SQL retrieving filtered value in subquery

in this cust_id is a foreign key and ords returns the number of orders for every customers
SELECT cust_name, (
SELECT COUNT(*)
FROM Orders
WHERE Orders.cust_id = Customers.cust_id
) AS ords
FROM Customers
The output is correct but i want to filter it to retrieve only the customers with less than a given amount of orders, i don't know how to filter the subquery ords, i tried WHERE ords < 2 at the end of the code but it doesn't work and i've tried adding AND COUNT(*)<2 after the cust_id comparison but it doesn't work. I am using MySQL
Use the HAVING clause (and use a join instead of a subquery).....
SELECT Customers.cust_id, Customers.cust_name, COUNT(*) ords
FROM Orders, Customers
WHERE Orders.cust_id = Customers.cust_id
GROUP BY 1,2
HAVING COUNT(*)<2
If you want to include people with zero orders you change the join to an outer join.
There is no need for a correlated subquery here, because it calculates the value for each row which doesn't give a "good" performance. A better approach would be to use a regular query with joins, group by and having clause to apply your condition to groups.
Since your condition is to return only customers that have less than 2 orders, left join instead of inner join would be appropriate. It would return customers that have no orders as well (with 0 count).
select
cust_name, count(*)
from
customers c
left join orders o on c.cust_id = o.cust_id
group by cust_name
having count(*) < 2

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 - Joining two tables without duplicates?

I have two tables that I am trying to join. One contains a list of customers, the other is a list of orders. I am trying to formulate a query that will allow me to select all of the customers listed in the table customers who have at least one order in the table orders. However, I do not want to get duplicates for those customers who have multiple orders. Any suggestions how I can accomplish this?
I know this is probably a common issue, however I have no idea what this type of query would be called so that I could search for an answer. Any suggestions would be greatly appreciated. Thanks.
It's much simpler than you may think:
select distinct(customer_id) from orders;
Edit: If you actually want to get the full info on the customer,
select * from customers where customer_id in (select distinct(customer_id) from orders);
Use:
SELECT c.*
FROM CUSTOMERS c
WHERE EXISTS (SELECT NULL
FROM ORDERS o
WHERE o.custeromid = c.id)
The IN clause is an alternative, but EXISTS works better for duplicates because it returns true on the first duplicate so it doesn't process the entire table.
select customers.id, customers.name, count(orders.id)
from customers
inner join orders on orders.customer_id = customers.Id
group by customers.id, customers.name
having count(orders.id) > 0
SELECT
c.id,
c.name
FROM
customer c
INNER JOIN order o ON o.customer_id = c.id
GROUP BY
c.id,
c.name
HAVING
COUNT(o.id) >= 1
Can't remember if HAVING or GROUP BY comes first.