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).
Related
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;
I'm trying to write a query on MySQL workbench that shows me when a customer last ordered from my client's website. I'm using two tables for this purpose. One is called "orders", which is updated with order time whenever a new order is placed and assigns a unique id to each new order. The other table is called "orders_customers_details", which is also updated whenever a new order is placed and contains the unique id (based on a combination of customer's email address and phone number) of the customer placing the order, as well as a key which corresponds to the orders table's id.
My problem is that the query I'm using is not returning every customer's most recent order. In the case of some customers, I'm being given the date of their fourth or fifth most recent order.
I'm left joining orders on orders_customer_details:
left join orders o
on ocd.id = o.customer_details_id
I've also tried using a left outer join, and the following join:
from orders o, orders_customers_details ocd
where o.customer_details_id = ocd.id
In order to retrieve the most recent order only, I'm grouping by customer_id HAVING max(order.id). FYI, order.id increases with the placement of each new order, so the order with the highest id is the most recent order.
I've also tried
SELECT customer_id, MAX(order.id)
and then grouped by customer_id, but to no avail.
Here's the entire code:
select customer_id, o.id as id_of_last_order, date(order_date) as
last_ordered, timestampdiff(day, order_date, now()) as
days_since_last_ordered
from orders o, orders_customers_details ocd
where o.customer_details_id = ocd.id
group by customer_id having max(o.id)
order by customer_id;
A typical method in MySQL is to use a correlated subquery to get the most recent order:
select ocd.customer_id, o.id as id_of_last_order,
date(o.order_date) as last_ordered,
timestampdiff(day, o.order_date, now()) as days_since_last_ordered
from orders o join
orders_customers_details ocd
on o.customer_details_id = ocd.id
where o.order_date = (select max(o2.order_date)
from orders o2 join
orders_customers_details ocd2
on o2.customer_details_id = ocd2.id
where oc2.customer_id = ocd.customer_id
)
order by ocd.customer_id;
Of course, if o.id is an auto-incrementing column, then the largest value is from the most recent date. If this is the case, then you can just use aggregation:
select ocd.customer_id,
max(o.id) as id_of_last_order,
date(max(o.order_date)) as last_ordered,
timestampdiff(day, max(o.order_date), now()) as days_since_last_ordered
from orders o join
orders_customers_details ocd
on o.customer_details_id = ocd.id
group by ocd.customer_id
order by ocd.customer_id;
In MySQL 8+, you would simply use window functions:
select *
from (select ocd.customer_id, o.id as id_of_last_order,
date(o.order_date) as last_ordered,
timestampdiff(day, o.order_date, now()) as days_since_last_ordered,
row_number() over (partition by ocd.customer_id order by o.order_date desc) as seqnum
from orders o join
orders_customers_details ocd
on o.customer_details_id = ocd.id
) ocd
where seqnum = 1
order by ocd.customer_id;
Using a correlated sub query in the where clause to find the most recent order by customer and tidying up the code then something like this
select customer_id, o.id as id_of_last_order, date(order_date) as last_ordered,
timestampdiff(day, order_date, now()) as days_since_last_ordered
from orders_customers_details ocd
join orders o on o.customer_details_id = ocd.id
where ocd_id = (select max(ocd_id) from orders_customers_details ocd1 where ocd1.customer_id = ocd_customer_id);
Though I cannot be certain without table definitions sample data etc..
Try this-
SELECT
customer_details_id customer_id,
MAX(o.id) AS id_of_last_order,
MAX(order_date) AS last_ordered,
TIMESTAMPDIFF(DAY, MAX(order_date), NOW()) AS days_since_last_ordered
FROM orders O
INNER JOIN orders_customers_details OCD
ON O.customer_details_id = OCD.id
GROUP BY customer_details_id
ORDER BY customer_details_id;
I have 3 tables: users, orders and order_prices with the latter containing the analysis of each order and I need to find the customers that re-ordered in a specific month, ( both tables have user_id, order_id, order_date, good_id(only order_prices) etc) my script is the following:
select o.system_id, o.user_id, o.date, o.id
from orders o
where o.date >= '2018-11-01' and o.date <= '2018-11-30' -- in the date range
AND o.user_id in
( -- has made the same order (content)
select op.user_id
from order_prices op join users u on u.id = op.user_id
where op.good_id in
(
select good_id
from order_prices
where user_id = o.user_id
)
And u.number_of_orders > 0 -- has ordered before
)
group by o.user_id
Well i am not sure if this even works logically, also I guess I need to find those users that their previous order was the same in content with their last order the above script doesn't take into consideration.... any suggestions MORE than welcome!
I am trying to find maximum orderdate(means most recent) and the second last orderdate(means second last purchase)
output looks like this
> Emailaddress MostRecentPurchase 2ndMostRecentPurchase totalorder
> xyz#gmail.com 1/29/2018 1/11/2018 $30
SELECT
Customers.EmailAddress,
'$'+CAST(SUM(Orders.PaymentAmount) AS VARCHAR(12)) as TotalOrdered
FROM Customers,Orders
WHERE Customers.CustomerID=Orders.CustomerID
AND Orders.OrderDate BETWEEN '01/01/2017 00:00' AND '12/31/2017 23:59'
AND Orders.OrderDate = ( SELECT MAX(Orders.OrderDate)
FROM Orders
WHERE OrderDate < ( SELECT MAX(OrderDate)
FROM Orders
)
)
GROUP BY Customers.EmailAddress
ORDER BY TotalOrdered DESC
Hmmm. How about this?
select c.customerid, min(o.orderdate), max(o.orderdate), sum(o.paymentamount)
from customers c cross apply
(select top (2) o.*
from orders o
where o.customerid = c.customerid
order by o.orderdate desc
) o
group by c.customerid;
You can also do this using row_number(), but I'm into practicing lateral joins today.
Note: This leaves out the date comparisons (because the text description does not mention them) and the final formatting for the amount.
EDIT:
The equivalent query with row_number() is probably a tad less efficient:
select c.customerid, min(o.orderdate), max(o.orderdate), sum(o.paymentamount)
from customers c join
(select o.*, row_number() over (partition by o.customerid order by o.oderdate desc) as seqnum
from orders o
) o
on o.customerid = c.customerid and o.seqnum <= 2
group by c.customerid;
Y
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