This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 6 years ago.
I have two tables. One is basic customer information and one is ordering information.
I'm trying to find the max(order date) with the order status of 'placed'or'cancelled'. I don't care if the status if placed or cancelled. I just want the most recent order.
The first table (Info)
CustomerID LAST NAME FIRST NAME
1 AB BOB
2 BC ROBERT
3 AA JOHN
The second table(order)
CustomerID Order Date Order Status
1 12/16/2016 placed
2 8/5/2016 cancelled
1 5/8/2015 cancelled
2 8/9/2016 placed
3 7/15/2016 cancelled
3 8/20/2015 placed
I want the result to be:
CustomerID FirstName LastName OrderDate OrderStatus
1 AB BOB 12/16/2016 placed
2 BA ROBERT 8/9/2016 placed
3 AA JOHN 7/15/2016 cancelled
Here are my SQL syntax
SELECT distinct Info.CustomerID, Info.Lastname,Info.Firstname
FROM INFO
INNER JOIN
(SELECT
order.CustomerID, LastOrderDate=max(OrderDate),order.OrderStatus
FROM Order
GROUP BY order.CustomerID, order.orderstatus)a
ON a.CustomerID=Info.CustomerID
This didn't work because it's grouping by order status which give me the max date of each all orderstatus. Then I tried
SELECT distinct Info.CustomerID, Info.Lastname,Info.Firstname, Order.OrderStatus
FROM INFO, Order
INNER JOIN
(SELECT
order.CustomerID, LastOrderDate=max(OrderDate)
FROM Order
GROUP BY order.CustomerID)a
ON a.CustomerID=Info.CustomerID
This didn't work either because it says the Info.CustomerID could not be bound.
Any helps? Thanks!
Can you try the following query:
SELECT c.customerid, c.last_name, c.first_name, o.order_status, MAX(order_date)
FROM customer c JOIN order ON c.customerid = o.customerid
WHERE o.order_status IN ('placed', 'cancelled')
GROUP BY c.customerid, c.last_name, c.first_name, o.order_status;
update
If you don't want the max date by order status then you can remove it from group by, e.g.:
SELECT c.customerid, c.last_name, c.first_name, MAX(order_date)
FROM customer c JOIN order ON c.customerid = o.customerid
WHERE o.order_status IN ('placed', 'cancelled')
GROUP BY c.customerid, c.last_name, c.first_name;
Another update
Following query would return both the records if an order gets placed and cancelled on the same day:
SELECT c.customerid, c.first_name, c.last_name, o.order_date, o.order_status
FROM customer c JOIN
(SELECT o1.customerid, o1.order_date, o1.order_status
FROM `order` o1 JOIN
(SELECT o.customerid, max(o.order_date) as max_date
FROM `order` o
WHERE o.order_status in ('placed', 'cancelled')
GROUP BY o.customerid) o2
ON o1.customerid = o2.customerid and o1.order_date = o2.max_date) o
ON c.customerid = o.customerid;
Here's the SQL Fiddle.
Related
Here's my orders table:
I want to select all orders excluding very first order of each customer (if customer has placed multiple orders).
So if a customer e.g. 215 has total 8 orders, then I will select his all last 7 orders excluding his very first order 70000 which was placed on 10 July 2017.
But if a customer e.g. 219 had placed only one order 70007, it must be selected by the query.
Using an anti-join approach:
SELECT o1.order_id, o1.customer_id, o1.order_date, o1.order_value
FROM orders o1
LEFT JOIN
(
SELECT customer_id, MIN(order_date) AS min_order_date, COUNT(*) AS cnt
FROM orders
GROUP BY customer_id
) o2
ON o1.customer_id = o2.customer_id AND
o1.order_date = o2.min_order_date
WHERE
o2.customer_site = 1 AND
(o2.customer_id IS NULL OR
o2.cnt = 1);
The idea here is to try to match each record in orders to a record in the subquery, which contains only first order records, for each customer. If we can't find a match, then such an order record cannot be the first.
You can try below -
select order_id,customer_id,order_date,order_Value
from tablename
group by order_id,customer_id,order_date,order_Value
having count(order_id)=1
union all
select order_id,customer_id,order_date,order_Value
from tablename a where order_date not in (select min(order_date) from tablename b
where a.customer_id=b.customer_id)
Solution
Dear #Tim Biegeleisen, your answer almost done. just add HAVING COUNT(customer_id)>1
So the query is below:
SELECT o1.order_id, o1.customer_id, o1.order_date, o1.order_value
FROM orders o1
LEFT JOIN (
SELECT customer_id, MIN(order_date) AS min_order_date
FROM orders
GROUP BY customer_id
HAVING COUNT(customer_id)>1
) o2
ON o1.customer_id = o2.customer_id AND
o1.order_date = o2.min_order_date
WHERE
o2.customer_id IS NULL;
I have this query that needs to be corrected. So right now I have 90% of what I need but this last step has been giving me trouble. I know on 1996-07-04 employee #5 visited my site for the first time, and rightfully get the first timer count as 1. On 1996-07-11 I know employee #5 visited the site again but we do not want to count him again.
SELECT
Orders.OrderDate,
COUNT(OrderDetails.OrderDetailID) AS NumSessions,
COUNT(DISTINCT Orders.EmployeeID)
FROM OrderDetails
LEFT JOIN Orders
ON OrderDetails.OrderID=Orders.OrderID
GROUP BY Orders.OrderDate
EXPECTED:
DATE NumSessions FirstTimers
1996-07-04 3 1
1996-07-11 3 0
ACTUAL:
DATE NumSessions FirstTimers
1996-07-04 3 1
1996-07-11 3 1
Maybe something like this (I can't guarantee it's syntactically correct, though):
SELECT
Orders.OrderDate,
COUNT(OrderDetails.OrderDetailID) AS NumSessions,
COUNT(DISTINCT Orders.EmployeeID WHERE Orders.OrderDate = (
SELECT MIN(b.OrderDate) FROM Orders b WHERE Orders.EmployeeID = b.EmployeeID)
)
FROM OrderDetails
LEFT JOIN Orders
ON OrderDetails.OrderID=Orders.OrderID
GROUP BY Orders.OrderDate
But I don't know how this would do performance-wise. :)
This is a little complicated. One method is to use a subquery to calculate the first order date and to use that in the logic:
SELECT o.OrderDate,
COUNT(od.OrderDetailID) AS NumSessions,
SUM( o.OrderDate = oe.FirstOrderDate ) as FirstTimers
FROM Orders o LEFT JOIN
OrderDetails od
ON O.OrderId = od.OrderId LEFT JOIN
(SELECT o.EmloyeeID, MIN(OrderDate) as FirstOrderDate
FROM Orders o
GROUP BY OrderDate
) o
ON oe.EmployeeId = o.EmployeeId
GROUP BY o.OrderDate
Im trying to get by subquery clientId of customer with most orders but only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
SELECT a.ClientName
FROM Clients as a
INNER JOIN Orders as b
ON a.Id=b.ClientId
WHERE b.ClientId
IN(SELECT b.ClientId,COUNT( b.ClientId) as MAKS FROM Orders as b
GROUP BY b.ClientId ORDER BY MAKS DESC)
Do we have some tools to handle this and how can i optimize this query? Thanks in advance.
You don't really need the inner join because you are asking for an ID that is the same in both tables,
SELECT ClientName FROM Clients
WHERE Id = (SELECT TOP 1 ClientId FROM Orders
GROUP BY ClientId
ORDER BY COUNT(ClientId) DESC)
Using Top 1, Count and Group By
SQL Server
SELECT Top 1 a.ClientName , count(b.orders_id) TotalOrders
FROM Clients as a
INNER JOIN Orders as b
ON a.Id=b.ClientId
GROUP BY a.client_name
order by TotalOrders desc
MySQL
SELECT a.ClientName , count(b.orders_id) TotalOrders
FROM Clients as a
INNER JOIN Orders as b
ON a.Id=b.ClientId
GROUP BY a.client_name
order by TotalOrders desc
LIMIT 1
There title does not quite describe well what i need from the query.
#sgeddes helped me come up with the following query but the query needs some alteration to accomplish my needs. I also modified the query slightly to not Select deleted customers but i couldn't change much due to the way this query is written is out my SQL knowledge.
SELECT d.customer_id,d.fname,d.lname,d.isactive,
o.lastdate,
Count(o2.order_id) AS 'total_orders'
FROM customers d
LEFT JOIN (SELECT MAX(order_id) order_id, customer_id
FROM orders
GROUP BY customer_id) m on d.customer_id = m.customer_id
LEFT JOIN orders o on m.order_id = o.order_id
LEFT JOIN orders o2 on d.customer_id = o2.customer_id
AND o2.balance > 0 AND o2.isActive > -1
WHERE d.user_id =945766 AND d.isActive > -1
AND o2.customer_id IS NULL
GROUP BY d.customer_id
I need the three following requirements.
count orders for customer with isActive > -1 (-1 = deleted)
Customers not in orders table (customer with no orders).
Customers with isActive = 0 and their corresponding order count
so briefly all I need is customer with isActive = 0 and to get an actual # for total_orders column.
In my attempt to better help you understand my requirement I created a SqlFiddle.
Please see my SqlFiddle
If your requirement is what you mentioned in your 3 points then you can use below queries-
If you want separate query then use below-
SELECT d.customer_id, d.fname, COUNT(o.order_id) AS Total_Orders
FROM customer d
LEFT JOIN orders o ON d.customer_id=o.customer_id
WHERE d.isActive > -1
GROUP BY d.customer_id;
SELECT DISTINCT d.customer_id, d.fname
FROM customer d
LEFT JOIN orders o ON d.customer_id=o.customer_id
WHERE o.customer_id IS NULL;
SELECT d.customer_id, d.fname, COUNT(o.order_id) AS Total_Orders
FROM customer d
LEFT JOIN orders o ON d.customer_id=o.customer_id
WHERE d.isActive = 0
GROUP BY d.customer_id;
If you want to merge them by union then use below-
SELECT 'isactive>-1' AS 'status', d.customer_id, d.fname, COUNT(o.order_id) AS Total_Orders
FROM customer d
LEFT JOIN orders o ON d.customer_id=o.customer_id
WHERE d.isActive > -1
GROUP BY d.customer_id
UNION ALL
SELECT DISTINCT 'Customer without Order' AS 'status', d.customer_id, d.fname, COUNT(o.order_id) AS Total_Orders
FROM customer d
LEFT JOIN orders o ON d.customer_id=o.customer_id
WHERE o.customer_id IS NULL
UNION ALL
SELECT 'isactive=0' AS 'status', d.customer_id, d.fname, COUNT(o.order_id) AS Total_Orders
FROM customer d
LEFT JOIN orders o ON d.customer_id=o.customer_id
WHERE d.isActive = 0
GROUP BY d.customer_id;
Note: Your main query is trying fetch latest order which can be for any other purpose, so if you provide your exact requirement if different from it then someone can help you.
I'm fairly new to more advanced SQL queries
Given the following tables and associated fields:
Person
PersonId, FirstName, LastName
Order
OrderId, PersonId, OrderDateTime
I want to write a query that will join both tables by PersonId and will retrieve every person and their most recent order.
So if James Doe (PersonId = 1) below has many orders in the orders table,
OrderId, PersonId, OrderDateTime
1 1 12/1/2013 9:01 AM
2 1 2/1/2011 5:01 AM
3 2 10/1/2010 1:10 AM
it will only take the most recent for his.
PersonId NameFirst NameLast OrderId OrderDateTime
1 James Doe 1 12/1/2013 9:01 AM
2 John Doe 3 10/1/2010 1:10 AM
I have been trying something like this
SELECT p.PersonID, o.OrderID, MAX(o.OrderDateTime) From Person p
JOIN Orders o ON p.PersonID = o.PersonID
GROUP BY p.PersonID,
Thanks
The inner query in this solution is a temporary table containing the most recent orders for each person. I join this back to the Orders table to get the fields you want, and then join again to the Person table.
SELECT p.PersonID, p.NameFirst, p.NameLast, o.OrderID, o.OrderDateTime
FROM Person p INNER JOIN Orders o
ON o.PersonId = p.PersonId
INNER JOIN
(
SELECT o1.PersonId, MAX(o1.OrderDateTime) AS maxTime
FROM Orders o1
GROUP BY o1.PersonId
) t
ON o.PersonId = t.PersonId AND o.OrderDateTime = t.maxTime
You can use variables to simulate ROW_NUMBER not available in MySQL:
SELECT p.PersonId, FirstName, LastName,
o.OrderId, o.OrderDateTime
FROM Person AS p
LEFT JOIN (
SELECT OrderId, OrderDateTime, PersonId,
#row_number := IF(#pid <> PersonId,
IF(#pid:=PersonId, 1, 1),
IF(#pid:=PersonId, #row_number+1, #row_number+1)) AS rn
FROM `Order`
CROSS JOIN (SELECT #row_number := 0, #pid := 0) vars
ORDER BY OrderDateTime DESC
) AS o ON p.PersonId = o.PersonId AND o.rn = 1
rn = 1 for the top level record within each PersonId slice of the derived table. Using this predicate in the ON clause of the LEFT JOIN we can match each row of Person to the most recent row of Order and obtain all Order fields.
Demo here
EDIT:
In SQL-Server the query looks like this:
SELECT p.*, o.OrderId, o.OrderDateTime
FROM Person AS p
LEFT JOIN (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY PersonId
ORDER BY OrderDateTime DESC) AS rn
FROM [Order]
) AS o ON p.PersonId = o.PersonId AND o.rn = 1
So the most recent order per every person. You could do something like this:
select p.*, o.*
from person p
inner join orders o on p.personId = o.personId
inner join (
-- get the max order per person
SELECT max(orderId) as orderId, personId
from orders
group by personId
) maxOrder on o.orderId = maxOrder.orderId
joining o onto maxOrder on the orderId filters the result set only to the orders that are also the maximum order per customer.
Note that I used the ID on the table rather than the datetime, as the ID is guaranteed to be unique (for help with joins) - this is not always available depending on the nature of the use in the table, but it looks like it should work for your case.