Working with multiple tables - mysql

I have these 4 tables with different rows and columns and I need to get certain values. I am having some difficulty with a few and cannot seem to get it.
customers table:
customerID: integer
fName: string
lName: string
items table:
itemID: integer
description: string
price: float
orders table:
orderID: integer
itemID: integer
aID: integer
customerID: integer
date: date
addresses table:
aID: integer
housenum: integer
streetName: string
town:string
state: string
zip:integer
The values I am looking to get are:
List the itemID and description of all items for which there is only one order.
Now I can get the itemID and description, however I am not sure how to nail it down to items only ordered once. I am thinking of using JOIN and GROUP BY but I cannot seem to find the correct combination.
SELECT itemID,
description
FROM items
GROUP BY itemID
List all customers who have shipped items to multiple addresses.
This one I have to use JOIN somehow, since the customerID would be the same but not the address?
SELECT customerID,
streetName,
housenum
FROM addresses
JOIN customers
ON addresses.streetName = customers.customerID
Return the total revenue generated by all of the orders.
Couldn't I just use:
SELECT SUM(price),
orderID
FROM items
JOIN orders
ON orders.orderID = items.price ?
List the first and last names of all customers who have had the same item shipped to at least two different addresses.
SELECT fName,
lName,
itemID,
streetName,
housenum
FROM addresses,
customers,
items,
somehow join multiple(2+)?

I may not be right with all the queries but I have found the items listed on the question very interesting and decided to give it a try.
items sold only once
SELECT o.itemID,
i.description
FROM orders o
LEFT JOIN items i
ON i.id = o.itemID
GROUP BY o.itemID
HAVING COUNT(*) = 1;
On this query, you need to join the the items table in order to retrieve the description and GROUP BY it by the itemID so you can specifically use COUNT for each itemID which we can then use with HAVING to obtain on the ones with 1 order.
Customer that have shipped to diff address
SELECT COUNT(DISTINCT addressID) as diff_address,
c.fName,
c.lName
FROM orders o
LEFT JOIN customers c
ON o.customerID = c.id
GROUP BY o.customerID
HAVING diff_address > 1;
Here I am using the DISTINCT function to eliminate duplicated address and GROUP BY the customerID so I can have a list of unique address per customer and again using HAVING we get the customers that have shipped products with more than 1 different address. Am also using JOIN on the customer table to obtain their needed information.
Total revenue
SELECT SUM(i.price) as total
FROM orders o
LEFT JOIN items i
ON o.itemID = i.id;
I guess this one is the simplest one here, by using JOIN on the items table, I am able to read each item price which I can then sum per order to have a gran total of sales.
List of customers that have shipped the same item to at least 2 different address
SELECT c.fName,
c.lName,
COUNT(oi.itemID) AS `total items with diff address >= 2`
FROM customers c
JOIN (SELECT customerID,
itemID,
COUNT(DISTINCT addressID) AS diff_address
FROM orders
GROUP BY itemID, customerID
HAVING diff_address >= 2
) AS oi
ON oi.customerID = c.id
GROUP BY c.id;
This one was the most complicated one, hope I can explain it. First I started it with the sub-query where I can list all items per customer that have multiple address, then by using JOIN on the customerID I can count how many different items that given customer have shipped with 2 or more diff address.
Here is a LIVE DEMO.
If you see any of the queries are not good or have any advice kindly do tell me so I can improve it.

Related

How can I use COUNT and GROUP BY in MYSQL while still displaying all individual rows?

I am just trying some practise exercises with MYSQL. I have a dataset where I would like to get the names of customers who ordered two or more different kinds of item and how many of each kind of item they bought.
The query below gives me a row for each name of the purchaser. However, I also want to display what types of items they bought and how many of them. Ideally I would like to have the same number of rows for each customer for how many different items they bought.
SELECT firstname, familyname, description, quantity
FROM customers c
JOIN orders o ON o.custID = c.custID
JOIN lineitems l on o.orderID = l.orderID
JOIN items i on l.itemID = i.itemID
GROUP BY firstname
HAVING count(description)
The query below does give me a row for each item, how many items that person bought, and the name of the purchaser. However, it does not filter for customers who only bought one specific item anymore.
SELECT firstname, familyname, description, quantity
FROM customers c
JOIN orders o ON o.custID = c.custID
JOIN lineitems l on o.orderID = l.orderID
JOIN items i on l.itemID = i.itemID
WHERE EXISTS(
SELECT *
FROM customers
GROUP BY firstname
HAVING count(description) >= 2)
Basically I would like to combine both approaches where there are multiple rows for specific item for each customer, while also filtering out customers who only bought one type of item.
If you are running MySQL 8.0, you can do a window count in a subquery and filter in the outer query, like:
SELECT *
FROM (
SELECT
c.firstname,
c.familyname,
i.description,
l.quantity,
COUNT(*) OVER(PARTITION BY c.custID) cnt
FROM customers c
JOIN orders o ON o.custID = c.custID
JOIN lineitems l on o.orderID = l.orderID
JOIN items i on l.itemID = i.itemID
) x
WHERE cnt > 1
Note: it is a good practice to prefix column names with the alias of the table they belong to; this makes the query more readable and avoid clashes when the same column name exists across tables. I made a few assumptions and updated the query accordingly.

How to retrieve the name of every customer and how many pizzas they have ordered?

I have to retrieve the name of every customer and how many pizzas they have ordered. NULL if no pizzas ordered.
SELECT blank 1
FROM blank 2(
SELECT blank 3 AS quantity
FROM ORDERS NATURAL JOIN ORDERCONTENTS
GROUP BY blank 4) as quantity
ON customers.customerID=quantity.customerID
ORDER BY name;
The database contains 5 tables in this layout:
Customers
columns
customerID,
name,
address,
phoneNumber,
email
Ordercontents
columns
orderID,
name,
quantity
orders
columns
orderID,
customerID,
postcode,
Date
products
columns
name,
price
stores
columns
postcode,
address,
phoneNumber
The numbered blanks have to be replaced to produce the outcome.
There shouldn't be a need for a sub-query/pseudo table. Use a left join on the three tables needed to get your answer.
SELECT
a.`name`,
IF(SUM(b.`quantity`) IS NULL,0,SUM(b.`quantity`)) as `pizzas_ordered`
FROM Customers a
LEFT JOIN `orders` o
ON a.`customerID` = o.`customerID`
LEFT JOIN `Ordercontents` b
ON o.`orderID` = b.`orderID`
GROUP BY a.`customerID`
ORDER BY a.`name`;
If you also need the dollar amount for each customer, that could be easily added.

Get 2 variables from tables which are connected not directly

I have these tables:
Customer = {id,firstname,last name,street,city}
Invoice = {id, customerid, total}
Item = {invoiceid, item, productid, quantity, cost}
Product = {id, name, price}
I would like to get the first name of the customer and the list of products what he bought.
I have created an sql code:
select customer.firstname, product.name from product
inner join item on item.productid = product.id
inner join invoice on invoice.id=item.invoiceid
inner join customer on customer.id=invoice.customerid
where customer.id=24
the customer.id is 24, because on this id I should get only 3 items' name.
Unfortunately, I am getting multiplication of these items.
What should I repair in my query?
I am getting multiplication of these items.
That is how relational databases work when you ask for a join. You get a Cartesian Product of the matching records.
However, MySQL has a facility that lets you put multiple values into a single field - it's called group_concat:
SELECT
customer.firstname
, GROUP_CONCAT(product.name SEPARATOR ', ')
FROM product
INNER JOIN item ON item.productid = product.id
INNER JOIN invoice ON invoice.id=item.invoiceid
INNER JOIN customer ON customer.id=invoice.customerid
WHERE customer.id=24
GROUP BY customer.id
Note the use of GROUP BY, which "merges" all rows for the same customer id into a single group, on which GROUP_CONCAT operates.

FInd the names of customers are interested in every artist

I am looking for customer names of customers who have an interest in all artists.
I understand that in relational algebra, I can use the division operator, however I do not understand the SQL format in doing so.
I have these tables with columns:
customer (customerID, firstname, lastname)
artist (artistID)
customer_interest_in_artists (artistID, customerID)
How would I go about doing this?
You could do this using a simple MIN() construct:
SELECT c.firstname, c.lastname, MIN(ci.customerID IS NOT NULL) AS interest_all
FROM artist
LEFT JOIN customer_interest_in_artists ci USING (artistID)
LEFT JOIN customer c USING (customerID)
GROUP BY c.customerID
HAVING interest_all = 1
You could either:
Identify the customers for whom the number of rows in the customer_interest_in_artists table is equal to the number of rows in the artists table.
or
Identify the customers for whom there does not exist a row in the customer_interest_in_artists table for one or more artists.
The first option os probably easiest to implement, as you can already probably get the number of rows per customer (hint: join, count(*), and group) and compare the number per customer with the number of rows in artists -- hint: having count(*) = (a subquery)
for ORACLE, I use this...
but i don't think mine is the most elegant of all answers, anyway, here it is!
SELECT c.FIRSTNAME, c.LASTNAME, c.CUSTOMERID
FROM DTOOHEY.CUSTOMER c, DTOOHEY.CUSTOMER_ARTIST_INT cai
WHERE c.CUSTOMERID = cai.CUSTOMERID
AND c.CUSTOMERID IN
(SELECT cai.CUSTOMERID
FROM DTOOHEY.CUSTOMER_ARTIST_INT cai
GROUP BY cai.CUSTOMERID
HAVING COUNT (*) = (SELECT COUNT (*) FROM DTOOHEY.ARTIST)
)
GROUP BY c.FIRSTNAME, c.LASTNAME, c.CUSTOMERID;
based on my limited knowledge, the flow of command is:
1) I am trying to get the customer ID, first name and last name of customer
2) I am getting it from the 2 tables (cai and c)
3) trying to join the 2 tables to give me a single data set
4) where the c.customerid is to be gathered in...
this is where the magic begins!!!
5) select the customerID (the single CustomerID)
6) from this table cai
7) group the result based on customerID, this is what gives the single CustomerID Value that you need...
8) having COUNT (*) - having the count of customerID value, to that of equal of the number of count of artists in the dtoohey.artist table.
the main logic is that the number of artist in the artist table (which is 11), exist in the CUSTOMER_ARTIST_INT in the same quantity. As such, we can tally the result of count from the ARTIST Table into the CUSTOMER_ARTIST_INT table.
I think these will useful to you
SELECT a.customerID, c.artistID from customer a
join customer_interest_in_artists b on a.customerID = b. customerID
join artist c on c.artistID = b.artistID
Thank you.

A cleaner way to do this SQL query

So have a list of customers, that have a list of customers.
I have 2 tables, customers and invoices.
Customer_id can be the same for my customers e.g
Client A
Customer #1
Client B
Customer #1
So invoice table can have customer_id of 1, for different customers.
The below query works, but seems a little messy. Any ideas to clean it up?
SELECT name,
sum(sub_total) total
FROM customers, invoices
WHERE customer_id = 1438 and
invoices.customer_id = 1438 and
customers.customer_id = invoices.customer_id
GROUP BY name
Order By total DESC;
If A = B and A = 1438 then B = 1438 you don't need to check it...
(Trust me on this one, I got 60+ in math in highschool)
SELECT name, sum(sub_total) total
FROM customers, invoices
WHERE invoices.customer_id = 1438
AND customers.customer_id = invoices.customer_id
GROUP BY name
ORDER BY total DESC;
Or with explicitly saying which type of JOIN you want:
SELECT name, sum(sub_total) total
FROM customers INNER JOIN invoices
ON customers.customer_id = invoices.customer_id
WHERE invoices.customer_id = 1438
GROUP BY name
ORDER BY total DESC;
you dont need the extra check...
SELECT name, sum(sub_total) total
FROM customers, invoices
WHERE
customer_id = 1438 and
customers.customer_id = invoices.customer_id
GROUP BY name order by total DESC;
I would strongly NOT do a group by on name... Say you have two customers named "Bill Smith", but one is customer ID 10, and the other is customer ID 785... You just combined them into a single person. You SHOULD be grouping by ID, not the name...
Now, that said, you are querying for only a single customer ID anyhow and will only ever return a single record. If what you are really trying to accomplish is getting ALL customers and the tot of their respective invoices, remove the where clause of the single customer. You can keep the group by ID, but show the actual customer's name. If you have multiple customers with the same total, you can sub-sort by their name, but the grouping is STILL based on just the customer's ID.
SELECT
c.name,
sum(i.sub_total) total
FROM
customers
JOIN invoices
on c.Customer_ID = i.Customer_ID
GROUP BY
c.customer_ID
Order By
total DESC,
c.Name