Trying to find 2nd last order date for the customer - sql-server-2008

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

Related

max(count ) from 2 tables mysql

Finding the city in which the most orders were sent leads to the assignment of the city and the number of orders (the named amount column). I have 2 tables the named Customers and Orders
SELECT Customers.City,count( Orders.OrderID) as amount
FROM voodoo.Customers
inner join voodoo.Orders on Customers.CustomerID=Orders.CustomerID
group by Customers.City
having amount >= all(select count(Orders.OrderID)
from voodoo.Customers
inner join voodoo.Orders on Customers.CustomerID=Orders.CustomerID
group by Customers.City);
tables
You don't need a subquery as you can just order by amount (descending) and limit the result to 1:
SELECT Customers.City, count(Orders.OrderID) as amount
FROM voodoo.Customers INNER JOIN voodoo.Orders
ON Customers.CustomerID=Orders.CustomerID
GROUP BY Orders.OrderID
ORDER BY amount DESC
LIMIT 1;
EDIT: as Thorsten Kettner pointed out, I made a copy & paste error; the correct version would GROUP BY Customers.City.
You are looking for the order count per city, not per order. So, don't group by order, but by city. For the ranking of the cities you can use RANK or DENSE_RANK.
SELECT city, amount
FROM
(
SELECT
c.city,
COUNT(o.orderid) AS amount,
RANK() OVER (ORDER BY COUNT(o.orderid) DESC) AS rnk
FROM voodoo.customers c
INNER JOIN voodoo.orders o ON o.customerid = c.customerid
group by c.city
) counted_and_ranked
WHERE rnk = 1;

How to limit to maximum count of rows in single table in MySQL 5.5

I have a single table, with the following columns:
Id BigInt
CustomerId BigInt
Order varchar(50)
DateOfOrder date
What I try to achieve is the following:
Getting all CustomerId, which have a specific string for the column order and the DateOfOrder is since 2018 and from this only the last two orders.
I started with the following SQL-Statement
Select o.CustomerId as CustomerId, o.Id as Id
from order o
where o.Order="Merchandise"
and year(o.DateOfOrder)>= 2018
order by o.DateOfOrder desc;
But how do I get only the 2 top orders of each CustomerId?
THX a lot in advance
what are you looking for is called row_number and you should use it with Subquery in order to get 2 top orders of each CustomerId based on DateOfOrder
SELECT
*
FROM
(
SELECT
o.customerid AS customerid,
o.id AS id,
row_number() over (
partition by o.customerid
ORDER BY o.DateOfOrder DESC
--ORDER BY o.Order DESC
) rn
FROM
ORDER o
WHERE
o.ORDER = "Merchandise"
AND year(o.dateoforder)>= 2018
)
WHERE
rn <= 2
for lower versions of Mysql that does not support row_number use correlated-subquery
SELECT o1.id ,
o1.customerid ,
o1.order ,
o1.dateoforder
FROM order O1
WHERE 2 >
(
SELECT Count(*)
FROM order O2
WHERE o1.customerid = o2.customerid
AND o1.order > o2.order )
AND
o1.ORDER="Merchandise"
AND year(o1.dateoforder)>= 2018
I think, I solved my problem.
Not beatuiful, but working.
If someone is interested in the solution:
First I get all oldest Orders into a Temporary Table.
Then I get second oldest orders, by using the id of the oldest orders, which are not allowed to be in the query The result is also stored in a temporary table..
And then, I union the results of the temporary tables to get my data, which I wanted to have.
Create Temporary Table Top_Orders
Select o.Id Id, o.CustomerId CustomerId, Max(o.DateOfOrder) OrderDate from order o where o.Order = "Merchandise" and year(o.DateOfOrder) >= 2018 group by CustomerId;
Create Temporary Table Top_Orders2
Select o.Id Id, o.CustomerId CustomerId, Max(o.DateOfOrder) OrderDate from order o where o.Order = "Merchandise" and year(o.DateOfOrder) >= 2018 and o.Id not in (Select Id from Top_Orders) group by CustomerId;
Select o1.CustomerID, o1.Id from order o1 where o1.Id in (Select t1.Id from Top_Orders t1) union Select o2.CustomerId, o2.Id from order o2 where o2.Id in (Select t2.Id from Top_Orders2 t2);
Drop Temporary Table Top_Orders;
Drop Temporary Table Top_Orders2;

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).

How To Return Rows Where The Date In One Column Is Closest To (Without Exceeding) The Date In Another Column

I've inherited a database that tracks--among a ton of other information--unit prices over time and orders placed for particular products. I need my query to return only the rows where the value in column effectiveDate is closest to without exceeding the value in column OrderDate (in order to find the unitprice at the time the order was placed). The linked image shows the latest iteration of my query and its results. I've also tried GROUP BY puph.productID, but the results returned have the earliest rather than the latest effectiveDate.
In short, how do I return only those rows with the latest effectiveDate at the time of the OrderDate?
mysql v8.0, you can use row_number().
select * from (select o.OrderID, o.OrderDate, puph.productID, puph.unitprice, puph.effectiveDate
, row_number() over (partition by o.orderID order by puph.effectiveDate desc) as rn
from orders orders
join `order details` od on o.OrderID = od.OrderID
join productunitpricehistory puph on od.ProductID = puph.productID
where puph.effectiveDate <= o.OrderDate
) t1
where t1.rn = 1
order by t1.OrderID
or older version of mysql, you can use max() function.
select o.OrderID, o.OrderDate, puph.productID, puph.unitprice
, max(puph.effectiveDate) as effectiveDate
from orders orders
join `order details` od on o.OrderID = od.OrderID
join productunitpricehistory puph on od.ProductID = puph.productID
where puph.effectiveDate <= o.OrderDate
group by o.OrderID, o.OrderDate, puph.productID, puph.unitprice
order by o.OrderID

Find the youngest customer with AT LEAST 1 purchase order

I need to write a query to find the youngest customer who bought atleast 1 product
Here is the data:
CUSTOMER:
ORDER_DETAIL:
This is my query so far:
SELECT c.CUSTOMERID, c.age, c.name
from (
SELECT CUSTOMERID, COUNT(ORDERID) as "totalOrder"
FROM FACEBOOK_ORDER_DETAIL
GROUP BY CUSTOMERID
HAVING COUNT(ORDERID) >=1) AS tbl
LEFT JOIN FACEBOOK_CUSTOMER c on c.CUSTOMERID = tbl.CUSTOMERID
order by c.age ;
However, above query gives me
But I need the list of customers with the minimum age.
If you really only want a single youngest customer, even should there be a tie, then use LIMIT:
SELECT c.CUSTOMERID, c.age, c.name
FROM CUSTOMER c
INNER JOIN FACEBOOK_ORDER_DETAIL o
ON c.CUSTOMERID = c.CUSTOMERID
ORDER BY
c.age
LIMIT 1;
This should work because if a customer joins to the order details table, it implies that he had at least one order.
If instead you want to find all youngest customers, including all ties, then a nice way to handle this uses the RANK analytic function:
SELECT DISTINCT CUSTOMERID, age, name
FROM
(
SELECT c.CUSTOMERID, c.age, c.name, RANK() OVER (ORDER BY c.age) rnk
FROM CUSTOMER c
INNER JOIN FACEBOOK_ORDER_DETAIL o
ON c.CUSTOMERID = o.CUSTOMERID
) t
WHERE rnk = 1;
Demo
For earlier versions of MySQL, we can use a subquery as a workaround for not having RANK:
SELECT DISTINCT c.CUSTOMERID, c.age, c.name
FROM CUSTOMER c
INNER JOIN FACEBOOK_ORDER_DETAIL o
ON c.CUSTOMERID = c.CUSTOMERID
WHERE c.age = (SELECT MIN(t1.age)
FROM CUSTOMER t1
INNER JOIN FACEBOOK_ORDER_DETAIL t2
ON t1.CUSTOMERID = t2.CUSTOMERID);
Demo
You only want columns from customers, so I would phrase this as:
select c.*
from (select c.*,
rank() over (order by age) as seqnum
from customers c
where exists (select 1
from facebook_order_detail fod
where fod.customerid = c.customerid
)
) c
where seqnum = 1;
In particular, this requires no duplicate elimination or aggregation, so it should be faster. And it can use an index on face_book_details(customerid) and also perhaps on customers(age, customerid).