SQL query how to compare string - mysql

There are two tables:
Customers:
ID
Name
Surname
City
Orders:
OrderId
CustomerId
Purchase
Price
I'm trying to find customer id,name,surname where he hasn't Purchase "Pizza".
Any help to fix my query? I tried with cp.Purchase != "Pizza" but doesn't work
SELECT DISTINCT ID,FirstName,LastName
FROM Customers c
INNER JOIN Orders cp ON c.ID = cp.OrderID
ORDER BY c.ID
WHERE cp.Purchase LIKE '%Pizza%'

try this
select * from Customers where Id not in (
select CustomerId From Orders WHERE cp.Purchase LIKE '%Pizza%'
)

The right query is
SELECT DISTINCT c.ID, c.Name, c.Surname
FROM Customers c JOIN Orders o on c.ID = o.CustomerID
WHERE c.ID <> ALL (
SELECT c2.ID
FROM Customers c2 JOIN Orders o2 on c.ID = o2.CustomerID
WHERE o2.Purchase = 'Pizza')
This is because you are looking for who never bought a pizza, so you will select data of customers which ID never appear (<> ALL) in orders table in a record where a pizza was bought.
By the way, check also SQL base, try understand before getting the answer.

Related

Using SUM with Joins in MySQL returns unexpected result

I have these tables : customers, customer_invoices, customer_invoice_details, each customer has many invoices, and each invoice has many details.
The customer with the ID 574413 has these invoices :
select customer_invoices.customer_id,
customer_invoices.id,
customer_invoices.total_price
from customer_invoices
where customer_invoices.customer_id = 574413;
result :
customer_id invoice_id total_price
574413 662146 700.00
574413 662147 250.00
each invoice here has two details (or invoice lines) :
first invoice 662146:
select customer_invoice_details.id as detail_id,
customer_invoice_details.customer_invoice_id as invoice_id,
customer_invoice_details.total_price as detail_total_price
from customer_invoice_details
where customer_invoice_details.customer_invoice_id = 662146;
result :
detail_id invoice_id detail_total_price
722291 662146 500.00
722292 662146 200.00
second invoice 662147 :
select customer_invoice_details.id as detail_id,
customer_invoice_details.customer_invoice_id as invoice_id,
customer_invoice_details.total_price as detail_total_price
from customer_invoice_details
where customer_invoice_details.customer_invoice_id = 662147;
result :
detail_id invoice_id detail_total_price
722293 662147 100.00
722294 662147 150.00
I have a problem with this query :
select customers.id as customerID,
customers.last_name,
customers.first_name,
SUM(customer_invoices.total_price) as invoice_total,
SUM(customer_invoice_details.total_price) as details_total
from customers
join customer_invoices
on customer_invoices.customer_id = customers.id
join customer_invoice_details
on customer_invoice_details.customer_invoice_id = customer_invoices.id
where customer_id = 574413;
unexpected result :
customerID last_name first_name invoice_total details_total
574413 terry amine 1900.00 950.00
I need to have the SUM of the total_price of the invoices, and the SUM of the total_price of the details for each customer. In this case I'm supposed to get 950 as total_price for both columns (invoice_total& details_total) but it's not the case. what am I doing wrong & how can I get the correct result please. The answers in similar topics don't have the solution for this case.
When you mix normal columns with aggregate functions (for example SUM), you need to use GROUP BY where you list the normal columns from the SELECT.
The reason for the excessive amount in total_price for invoices is that the SUM is also calculated over each detail row as it is part of the join. Use this:
select c.id as customerID,
c.last_name,
c.first_name,
SUM(ci.total_price) as invoice_total,
SUM((select SUM(d.total_price)
from customer_invoice_details d
where d.customer_invoice_id = ci.id)) as 'detail_total_price'
from customers c
join customer_invoices ci on ci.customer_id = c.id
where c.id = 574413
group by c.id, c.last_name, c.first_name
db-fiddle
I used join against sub queries and then did a sum on the sums
SELECT c.id as customerID,
c.last_name,
c.first_name
SUM(i.sum) as invoice_total,
SUM(d.sum) AS details_total
FROM customers c
JOIN (SELECT id, customer_id, SUM(total_price) AS sum
FROM customer_invoices
GROUP BY id, customer_id) AS i ON i.customer_id = c.id
JOIN (SELECT customer_invoice_id as id, SUM(total_price) AS sum
FROM customer_invoice_details
GROUP BY customer_invoice_id) AS d ON d.id = i.id
WHERE c.id = 574413
GROUP BY c.id, c.name
The issue is in the joining logic. The table customers is used as the driving table in the joins. But in the second join, you are using a derivative key column from the first join, to join with the third tables. This is resulting in a Cartesian output doubling the records from the result from the nth-1 join, which is leading to customer_invoices.total_price getting repeated twice, hence the rolled up value of this field is doubled.
At a high level I feel that the purpose of rolling up the prices is already achieved in SUM(customer_invoice_details.total_price).
But if you have a specific project requirement that SUM(customer_invoices.total_price) should also be obtained and must match with SUM(customer_invoice_details.total_price), then you can do this:
In a separate query, Join customer_invoice_details and customer_invoices. Roll up the pricing fields, and have a result such that you have only one record for one customer ID.
Then use this as a sub-query and join it with the customers table.
You are aggregating along multiple dimensions. This is challenging. I would suggest doing the aggregation along each dimension independently:
select c.id as customerID, c.last_name, c.first_name,
ci.invoice_total,
cid.details_total
from customers c join
(select ci.sum(ci.total_price) as invoice_total
from customer_invoices ci
group by ci.customer_id
) ci
on ci.customer_id = c.id join
(select ci.sum(cid.total_price) as details_total
from customer_invoices ci join
customer_invoice_details cid
on cid.customer_invoice_id = ci.id
group by ci.customer_id
) cid
on cid.customer_id = c.id
where c.id = 574413;
A faster version (for one customer) uses correlated subqueries:
select c.id as customerID, c.last_name, c.first_name,
(select ci.customer_id, sum(ci.total_price) as invoice_total
from customer_invoices ci
where ci.customer_id = c.id
) as invoice_total,
(select ci.customer_id, sum(cid.total_price) as details_total
from customer_invoices ci join
customer_invoice_details cid
on cid.customer_invoice_id = ci.id
where ci.customer_id = c.id
) as details_total
from customers c
where c.id = 574413;

Error 1064 - In statement

I am trying to show the average price of products bought by customers from ‘Tucson’, however this query returns null even though there are two customers that have placed orders from Tuscon.
select AVG(product_price) from product where product_id in
(select product_id from orderline where order_id in
(select order_id from ordertable where cust_id in
(Select cust_id from customer where city = 'Tuscon')))
You're using fom instead of from in your query: select order_id fom ordertable where cust_id in
This should be select order_id from ordertable where cust_id in
fom is not a recognized keyword. The MySQL parser doesn't know what to do with that, so it throws an error about "invalid syntax".
Consider using join operations in place of nested IN subqueries. If we are guaranteed:
product_id is unique in product table
order_id is unique in ordertable table
cust_id is unique in customer table
then we can get an equivalent result set, the average of the price of distinct products that were ordered...
SELECT AVG(p.product_price)
FROM ( SELECT l.product_id
FROM orderline l
JOIN ordertable o
ON o.order_id = l.order_id
JOIN customer c
ON c.cust_id = o.cust_id
WHERE c.city = 'Tuscon'
GROUP BY l.product_id
) q
JOIN product p
ON p.product_id = l.product_id
If we want that "average price" of all products ordered (a different result, with the average taking into account the number of times a product was ordered...then we could use a query like this:
SELECT AVG(p.product_price)
FROM product p
JOIN orderline l
ON l.product_id = p.product_id
JOIN ordertable o
ON o.order_id = l.order_id
JOIN customer c
ON c.cust_id = o.cust_id
WHERE c.city = 'Tuscon'

SQL Querying in multiple tables

I have two tables, customers and Orders
Customers columns are
CustomerID,
Username,
Password,
Firstname,
Surname,
Email,
Mobile
Orders columns are
OrderID,
CustomerID,
Date,
Time,
Price,
Complete
I want to select all of the firstname and surnames from all the orders that have been complete. And yes It could be so that [0] = John Smith and [1] also = John Smith.
What I was thinking was
SELECT FirstName, Surname from order, customers
WHERE Complete = 'Yes' AND order.CustomerID = customer.CustomerID;
So first it looks at if the order is complete. If it is then it will look at the customer ID, then it will go to customers and get the firstname and surname of that customer then store it in the datatable.
Thank you for any help!!!
You could use EXISTS, the following query will return all customers that don't have incomplete (=0) orders:
select c.firstname, c.lastname
from customers c
where
not exists (select * from orders o
where c.customerid = o.orderid
and o.complete = 'No')
but it will also return customers that have no orders. If you want to exclude customers with no orders you could use an additional exist clause:
select c.firstname, c.lastname
from customers c
where
not exists (select * from orders o
where c.customerid = o.orderid
and o.complete = 'No')
and exists (select * from orders o where c.customerid = o.orderid)
or a group by clause:
select c.firstname, c.lastname
from customers c inner join orders o on c.customerid = o.customerid
group by c.customerid, c.firstname, c.lastname
having sum(o.complete='No') = 0
This will give you list of Firstname, Surname even though they don't have orders.
SELECT Customers.Firstname, Customer.Surname
FROM Customers, Orders
WHERE Orders.Complete = 'Yes'
LEFT JOIN Customers.CustomerID = Orders.CustomerID
I'd go with this personally:
SELECT c.Firstname, c.Surname FROM Customers c
INNER JOIN Orders o
ON c.CustomerID=o.CustomerID
WHERE o.Complete='Yes'
I like to be as explicit as possible with my queries, so anyone else who has to read my code understands the what, why and how. Though shouldn't you also select something to identify the order? Otherwise all you have is a list of names.
--Unique list of customer id, customer first name and customer surname.
SELECT DISTINCT
customers.customerid
, customers.firstname
, customers.surname
FROM orders
INNER JOIN customers
ON
customers.customerid = orders.customerid
AND orders.complete = 'Yes'
--Unique list of customer first name and customer surname, regardless
--if same names are tied to different customerid.
SELECT DISTINCT
customers.customerid
, customers.firstname
, customers.surname
FROM orders
INNER JOIN customers
ON
customers.customerid = orders.customerid
AND orders.complete = 'Yes'
Remove DISTINCT word if you want duplicates.

Mysql query select non-members

I have 2 tables:
Customers:
- ID
- NAME
Modulemembers:
- ID
- customer_id
- enabled
I use this to enable a function of my site for some customers.
I have a form where the admin of the site can add the module for a customer, so i need a query that looks for customers that are NOT member of the modulemembers table.
I made this:
SELECT customers.id,
customers.name
FROM customers,
modulemembers
WHERE customers_id != modulemembers.cust_id
ORDER BY customers.name
but it does not work. What am I doing wrong?
You can use NOT EXISTS in the WHERE clause to get the result:
SELECT c.id,
c.name
FROM customers c
WHERE not exists (select customer_id
from modulemembers m
where c.id = m.customer_id)
order by c.name
You can use a LEFT OUTER JOIN and filter based on NULLs. The query below will pull in all results from the customers table and the modulemembers table. If there is not a match in the modulemembers table then the custid will be NULL.
SELECT c.id, c.name
FROM customers c
LEFT OUTER JOIN modulemembers m ON c.customers_id = m.cust_id
WHERE m.custid IS NULL
ORDER BY c.name
Try something like this:
SELECT c.ID AS customerId,
c.NAME AS fullName,
m.ID AS memberId
FROM Customers AS c
LEFT JOIN Modulemembers AS m ON m.customer_id = c.ID
WHERE m.ID IS NULL;

Sql retrieve fields without a relation in another table

I have 2 tables: Customers and Orders. A customer has many orders and an order belongs to a customer. The order can be approved (marked by a field approved_at).
I want to retrieve all the customers without any approved order. This includes customers without any order and customers with orders that aren't approved (approved_at = null).
Can I do this in a single query without subqueries?
SELECT ..., COUNT(Orders.id) AS cnt
FROM Customers
LEFT JOIN Orders ON (Customers.id = Orders.Customer_id) AND (Orders.approved_at is null)
HAVING cnt = 0
SELECT c.*
FROM Customers c
LEFT JOIN Orders o ON c.ID = o.CustomerID
WHERE o.ID IS NULL OR c.Approved_at IS NULL