Using IFNULL function mysql to set null to zero - mysql

I have tables on my database with the following schema:
customers (customerID: integer, fName: string, lName: string)
items (itemID: integer, description: string, price: integer)
orders (orderID: integer, itemID: integer, aID: integer, customerID:integer, date: date)
and the following code:
SELECT c.customerID, COUNT(DISTINCT o.orderID) AS number_of_orders,
ROUND(SUM(i.price) / COUNT(DISTINCT o.orderID),2) AS average
FROM customers c
LEFT JOIN orders o
ON o.customerID = c.customerID
AND o.date >= '2013-03-01'
AND o.date < '2013-04-01'
LEFT JOIN items i
ON o.itemID = i.itemID
GROUP BY c.customerID
which returns three values: customer ID, number of orders per customer, and average spending per customer.
With the code as it is now, the average spending per customer is returned as blank (null).
I am having trouble using the IFNULL function to set the average spending per customer to 0.00 if the customer did not order anything in march 2013 (i.e., if number of orders per customer in march is zero).
Any help will be very much appreciated!

Without seeing your IFNULL attempts, this logic works for me:
SELECT c.customerID,
COUNT(DISTINCT o.orderID) AS number_of_orders,
ROUND(IFNULL( (SUM(i.price) / COUNT(DISTINCT o.orderID)), 0.00),2) AS average,
FORMAT(IFNULL(ROUND( (SUM(i.price) / COUNT(DISTINCT o.orderID)),2), 0), 2) AS averageWithFormat
FROM customers c
LEFT JOIN orders o ON o.customerID = c.customerID AND o.date >= '2013-03-01' AND o.date < '2013-04-01'
LEFT JOIN items i ON o.itemID = i.itemID
GROUP BY c.customerID
Returns: 0.00
I have two entries for average in my statement because in my local MySQL Workbench, the first one returns with two decimal precision but doesn't in SQLFiddle. I added the second entry with the FORMAT syntax to force the two decimal precision, if needed.

You use case maybe better off using the IF() function.
SELECT c.customerID, COUNT(DISTINCT o.orderID) AS number_of_orders,
IF(COUNT(o.orderID) > 0,ROUND(SUM(i.price) / COUNT(DISTINCT o.orderID),2),0) AS average
FROM customers c
LEFT JOIN orders o
ON o.customerID = c.customerID
AND o.date >= '2013-03-01'
AND o.date < '2013-04-01'
LEFT JOIN items i
ON o.itemID = i.itemID
GROUP BY c.customerID

Related

Finding first order in a single year

I'm trying to determine how many new people made an order in 2018. This looks straight forward enough but there is an error with putting calculated fields in the WHERE statement.
SELECT DISTINCT COUNT(c.customer_id)
FROM Customer c
LEFT JOIN
Orders o ON c.customer_id=o.customer_id
WHERE MIN(order_date) > '2017-12-31'
AND MIN(order_date) < '2019-01-01';
You can achieve this by putting a sequence number to the orders and then selecting the first row for each customer. Although, I'm not really sure why you're performing a count of the orders when you just want to consider the first orders. Nevertheless the below should work just fine.
SELECT count(res.customer_id) FROM (
SELECT c.customer_id,
ROW_NUMBER() OVER (PARTITION BY c.customer_id ORDER BY o.order_date ASC) row_num
FROM Customer c
LEFT JOIN Orders o ON c.customer_id=o.customer_id
WHERE o.order_date > '2017-12-31'
AND o.order_date < '2019-01-01'
) res WHERE res.row_num=1
Join with a subquery that finds the customers that were new in 2018.
SELECT COUNT(DISTINCT o.customer_id)
FROM Orders o
JOIN (
SELECT DISTINCT customer_id
FROM Orders
GROUP BY customer_id
HAVING MIN(order_date) > '2017-12-31'
) o1 ON o1.customer_id = o.customer_id
WHERE o.order_date < '2019-01-01';
There's also no need to join with Customers, since the customer ID is in Orders.
And the correct way to get the distinct count is COUNT(DISTINCT o.customer_id), not DISTINCT COUNT(o.customer_id).

MYSQL adding OR clause in left join slowing up the query

I have two database tables: orders and customers.
I'm running a SQL to get all orders in June month.
If the Ship To and Bill To email are different, we are inserting two different records with both emails to customers table.
select o.order_id
, o.total
, c.customer_email
from orders o
left
join customers c
ON o.bill_email = c.customer_email
OR o.ship_email = c.customer_email
where DATE(o.order_date) >= '2020-06-01'
But this SQL is taking too much time to load because of the condition,
ON o.bill_email=c.customer_email
OR o.ship_email=c.customer_email
How can I add both conditions in ON clause?
Any help would be appreciated.
Use two left joins and put the results in separate columns rather than rows:
select o.order_id, o.total, cb.customer_email, so.customer_email
from orders o left join
customers cb
on o.bill_email = cb.customer_email left join
customers cs
o.ship_email = cs.customer_email
where o.order_date >= '2020-06-01';
Note that the date() function is not needed.
That said, this seems more easily expressed as:
select o.order_id, o.total, o.bill_email
from orders o
where o.order_date >= '2020-06-01'
union all
select o.order_id, o.total, o.ship_email
from orders o
where o.order_date >= '2020-06-01' and s.ship_email <> o.bill_email;

SQL find highest date in all entries with a date older than x

I try to obtain a list of all customers my company has not had any assignments for in the last year.
SELECT MAX(assignment_date), full_name
FROM assignments
CROSS JOIN customers
WHERE assignments.customer_id = customers.id
AND assignment_date < '2017-01-01' -- Dynamic value from backend
GROUP BY full_name
ORDER BY assignment_date DESC
This does not seem to work as intended however, since it only returns some customers we did have assignments for in that timeframe. How would I go about implementing such a feature?
Try this code:
SELECT MAX(assignment_date), full_name
FROM customers
where id not in (SELECT id FROM customers inner join assigments on customers.id = assignments.customer_id WHERE assignment_date > '2017-01-01' )
This will return all customers in your database and remove all of them who did have assigments in last year. You should get all customers without assigments before '2017-01-01' as a result
left join customers to assignments where assignments.customer_id IS NULL
and assignment_date greater than '2017-01-01' i.e.
SELECT MAX(assignment_date), full_name
FROM assignments
WHERE assignments.customer_id IN
(SELECT customers.id
FROM customers
LEFT JOIN assignments ON assignments.customer_id = customers.id
WHERE assignments.customer_id IS NULL
AND assignment_date > '2017-01-01')
GROUP BY full_name
ORDER BY assignment_date DESC
I would suggest left join, group by and having:
SELECT MAX(assignment_date), full_name
FROM customers c LEFT JOIN
assignments a
ON a.customer_id = c.id
GROUP BY c.full_name
HAVING MAX(a.assignment_date) < '2017-01-01' OR
MAX(a.assignment_date) IS NULL
ORDER BY MAX(assignment_date) DESC;
It seems you want to show all customers with their last assignment date, but you want to restrict that list to customers that had no assignment since 2017-01-01. This means the dates you will be showing will be null for those customers who had never had an assignment and a date before 2017-01-01 for the others.
So outer join the last dates to the customers and only keep the rows where that date is before 2017-01-01 or null:
select c.full_name, a.max_date
from customers c
left join
(
select customer_id, max(assignment_date) as max_date
from assignments
group by customer_id
) a on a.customer_id = c.customer_id
where a.max_date < date '2017-01-01'
or a.max_date is null
order by a.max_date desc;

Calculating orders and quantity for sql query

Good afternoon Stack Overflow community! I'm pretty new to sql and writing queries, so if someone could just double check my work and maybe pass along any helpful tips at how I might go about coding it better? I've pasted along the purpose of the query and my code - I appreciate the help and feedback thanks!
Basically the query should create a report of customers living in Europe who have recently placed an order totaling more than $5,000. Order total must reflect the quantity of the products the customer ordered, which should also have a discount and order total.
Thanks again for the help!
select c.CustomerID, c.Country, sum (od.Discount * od.UnitPrice)'Total'
from Customers c
inner join Orders o
on c.CustomerID = o.CustomerID
inner join OrderDetails od
on o.OrderID = od.OrderID
where c.Country in ('germany', 'UK', 'sweden', 'france', 'spain', 'switzerland','austria', 'italy', 'belgium', 'norway', 'denmark', 'finland', 'poland')
group by c.Country, c.CustomerID
having sum ('Total' * od.Quantity) <= 5000
order by total desc
It looks good, but for a couple of points:
1) Your Total may be wrong
2) You must reverse the comparison operator:
select c.CustomerID, c.Country, sum (od.Discount * od.UnitPrice * od.Quantity) 'Total'
from Customers c
inner join Orders o
on c.CustomerID = o.CustomerID
inner join OrderDetails od
on o.OrderID = od.OrderID
where c.Country in ('germany', 'UK', 'sweden', 'france', 'spain',
'switzerland','austria', 'italy', 'belgium', 'norway', 'denmark',
'finland', 'poland')
and o.OrderDate >= '2017-03-01' -- for example
group by c.Country, c.CustomerID
having sum ( od.Discount * od.UnitPrice * od.Quantity) >= 5000
order by total desc

JOIN VS SUBQUERY

I need to do this but with a subquery, not a join. My problem is, how can I use a subquery to display another column? I could grab the info from there, but I'll be missing the order_date column from the orders table. Can I use a subquery to display it?
SELECT CONCAT(c.customer_first_name, ' ' , c.customer_last_name) AS customer_name, MAX(o.order_date) AS recent_order_date
FROM customers AS c
JOIN orders AS o
ON c.customer_id = o.customer_id
GROUP BY customer_name
ORDER BY MAX(o.order_date) DESC
It's not at all clear what resultset you are trying to return, but it looks an awful like the like the ubiquitous "latest row" problem.
The normative pattern for the solution to that problem is to use a JOIN to the inline view. If there's not a unique constraint, you run the possibility of returning more than one matching row.
To get the latest order (the row in the orders table with the maximum order_date for each customer, assuming that the (customer_id, order_date) tuple is unique, you can do something like this:
SELECT o.*
FROM ( SELECT n.customer_id
, MAX(n.order_date) AS latest_order_date
FROM orders n
GROUP BY n.customer_id
) m
JOIN orders o
ON o.customer_id = m.customer_id
AND o.order_date = m.latest_order_date
If you want to also retrieve columns from the customers table based on the customer_id returned from orders, you'd use a JOIN (not a subquery)
SELECT CONCAT(c.customer_first_name,' ',c.customer_last_name) AS customer_name
, c.whatever
, o.order_date AS recent_order_date
, o.whatever
FROM ( SELECT n.customer_id
, MAX(n.order_date) AS latest_order_date
FROM orders n
GROUP BY n.customer_id
) m
JOIN orders o
ON o.customer_id = m.customer_id
AND o.order_date = m.latest_order_date
JOIN customers c
ON c.customer_id = o.customer_id
ORDER BY o.order_date DESC, o.customer_id DESC
As I mentioned before, if a given customer can have two orders with the exact same value for order_date, there's potential to return more than one order for each customer_id.
To rectify that, we can return a unique key from the inline view, and use that in the join predicate to guarantee only a single row returned from orders.
(NOTE: this approach is specific to MySQL, with this syntax, other RDBMS will throw an error that essentially says "the GROUP BY must include all non-aggregates". But MySQL allows it.)
SELECT CONCAT(c.customer_first_name,' ',c.customer_last_name) AS customer_name
, c.whatever
, o.order_date AS recent_order_date
, o.whatever
FROM ( SELECT n.customer_id
, MAX(n.order_date) AS latest_order_date
, n.order_id
FROM orders n
GROUP BY n.customer_id
) m
JOIN orders o
AND o.customer_id = m.customer_id
AND o.order_date = m.latest_order_date
AND o.order_id = n.order_id
JOIN customers c
ON c.customer_id = o.customer_id
ORDER BY o.order_date DESC, o.customer_id DESC
I am not really sure i understand your question, but i think this works... (not tested though...)
SELECT
(
SELECT
CONCAT(c.customer_first_name, ' ' , c.customer_last_name)
FROM
customers c
WHERE
c.customer_id = o.customer_id
LIMIT 1
) AS customer_name,
MAX(o.order_date) AS recent_order_date
FROM
orders o
GROUP BY
customer_name
ORDER BY
MAX(o.order_date) DESC