New record after a year of no records - sql

I want to find returning customers who have placed an order after a year without orders. I have managed the below but am having a tough time adding the year gap. Something like "and count of orders between dates = 0"... any ideas would be appreciated, I cant seem to figure out the required syntax at all.
SELECT
Min(Orders.[Order Date]) AS [MinOfOrder Date],
Max(Orders.[Order Date]) AS [MaxOfOrder Date],
Orders.CustomerID
FROM
Customers
INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID
GROUP BY
Orders.CustomerID
HAVING
(
((Min(Orders.[Order Date])) < Date() -365)
AND ((Max(Orders.[Order Date])) > Date() -30)
);

You can use exists and not exists to get the first order after a year gap:
select o.*
from orders as o
where exists (select 1
from orders as o2
where o2.customerid = o.customerid and
o2.orderdate < dateadd("yyyy", o.orderdate, -1)
) and
not exists (select 1
from orders as o2
where o2.customerid = o.customerid and
o2.orderdate >= dateadd("yyyy", o.orderdate, -1) and
o2.orderdate < o.orderdate
);
You can join in the customers information if you need that.

Selecting records based on values in other records is tricky. Use a correlated subquery to pull value from another record. Need a unique identifier field - autonumber should serve. Consider:
Query1:
SELECT Orders.*, (SELECT Max(OrderDate)
FROM Orders AS Dupe
WHERE Dupe.CustomerID = Orders.CustomerID
AND Dupe.ID < Orders.ID) AS PrevOrderDate
FROM Orders;
Query2:
SELECT Query1.* FROM Query1 WHERE ((([OrderDate]-[PrevOrderDate])>365));

Related

Challenging PostgreSQL SELECT Statement for Northwind Database (NEED HELP - BEGINNER)

I began learning SQL about a month ago and my dad has been giving me practice queries to run
with the Northwind database to help practice my DML. This most recent one he gave me was as follows:
-- Return Month, Product name, SalesForMonth for highest selling product in each
-- month in 1997. (see issues with creating said query below)
(I am currently using PostgreSQL on pgAdmin4)
I was able to come up with the following query that returns the required columns with the correct information for ONLY a single month:
SELECT CAST( EXTRACT( MONTH FROM o.orderdate) AS integer) AS Month, p.productname,
ROUND(CAST(SUM(od.quantity * od.unitprice) AS numeric), 2) SalesForMonth
FROM order_details od
INNER JOIN orders o ON od.orderid = o.orderid
INNER JOIN products p ON od.productid = p.productid
WHERE EXTRACT( YEAR FROM o.orderdate) = 1997 AND EXTRACT( MONTH FROM o.orderdate) = 1
GROUP BY Month, p.productname
ORDER BY salesformonth DESC
LIMIT 1
By making 12 of these queries and changing the extract-month bit in the WHERE statement from 1-12 and UNIONing them all together, I can produce the desired result but I wondered if there was an easier way that I was missing to display the same result but only using 1 query. Interested to see what y'all can come up with.
SIDE NOTE: My initial thought is that it has something to do with subqueries because what you're effectively trying to do is display the MAX(SUM(values)) but can't actually do that since you can't nest aggregate function.
Your query gives you the top selling product for a given month, and you want the same logic for multipe months at once.
Starting from your existing and working query, a simple approach is to use WITH TIES:
SELECT DATE_TRUNC('month', o.orderdate) DateMonth,
p.productname,
ROUND(CAST(SUM(od.quantity * od.unitprice) AS numeric), 2) SalesForMonth
FROM order_details od
INNER JOIN orders o ON od.orderid = o.orderid
INNER JOIN products p ON od.productid = p.productid
WHERE o.orderdate >= DATE '1997-01-01' AND o.orderdate < DATE '1998-01-01'
GROUP BY DateMonth, p.productname
ORDER BY RANK() OVER(
PARTITION BY DATE_TRUNC('month', o.orderdate)
ORDER BY SUM(od.quantity * od.unitprice) DESC
)
FETCH FIRST ROW WITH TIES
We can also use DISTINCT ON:
SELECT DISTINCT ON (DateMonth)
DATE_TRUNC('month', o.orderdate) DateMonth,
p.productname,
ROUND(CAST(SUM(od.quantity * od.unitprice) AS numeric), 2) SalesForMonth
FROM order_details od
INNER JOIN orders o ON od.orderid = o.orderid
INNER JOIN products p ON od.productid = p.productid
WHERE o.orderdate >= DATE '1997-01-01' AND o.orderdate < DATE '1998-01-01'
GROUP BY DateMonth, p.productname
ORDER BY DateMonth, SalesForMonth DESC

SQL Query for counting >2 orders in a particular year

I have two tables. salesreps with columns: SRepID, SRepName, SRepPhone
orders with columns: OrderID, CustomerID, SRepID, OrderDate
I need to return all the SRepName who have handled at least two orders in the year 2019.
I am completely lost on this one and would love a push in the right direction.
I'm doing this in SQL Workbench.
select SRepName from salesReps
inner join Orders on salesReps.SRepID = orders.SRepID
where orderDate >= '2019-01-01' and orderDate < '2020-01-01'
group by sRepName
having count(OrderId) >1
You can use a correlated subquery to count the number of orders per sales rep:
select s.*
from salesrep s
where (
select count(*)
from orders o
where
o.repId = s.sRepId
and o.orderDate >= '2019-01-01'
and o.orderDate < '2020-01-01'
) >= 2
Or you can use aggregation. This requires you to enumerate the columns that you want to return, but also gives you the possibility to use aggregate functions on the orders:
select
s.sRepId,
s.sRepName,
count(*) no_orders,
max(o.OrderDate) last_order_date
from salesrep s
inner join orders o on o.repId = s.sRepId
where o.orderDate >= '2019-01-01' and o.orderDate < '2020-01-01'
group by s.sRepId, s.sRepName
having count(*) > 1
select SRepName
,count(OrderID)
from salesreps s
inner join orders o
on s.SRepID = o.SRepID
where year(OrderDate) = '2019'
group by SRepName
having count(OrderID) >= 2

joining tables , data between dates

I am practicing with northwind database: I am quite new to sql.
Question I am trying to solve is :
Q. Total Sales for each customer in October 1996 (based on OrderDate). Show the result in CustomerID, CompanyName, and [total sales], sorted in [total sales] in Decending order.
I have used this code but doesn't seems to be correct , please advise.
select c.customerid , c.companyname , o.orderdate , sum(od.unitprice *od.Quantity*1-od.Discount) as totalsales
from customers as c , orders as o , [Order Details] as od
where o.customerid = c.CustomerID
and o.OrderID = od.OrderID
and o.OrderDate >= '1996/10/01' and o.orderdate <= '1996/10/31'
group by c.customerid , c.companyname, o.orderdate
order by totalsales desc
;
*******************************
I suspect is it just the method of calculation inside the sum function
SELECT
c.customerid
, c.companyname
, o.orderdate
, SUM((od.unitprice * od.Quantity) * (1 - od.Discount)) AS totalsales
FROM customers AS c
INNER JOIN orders AS o ON o.customerid = c.CustomerID
INNER JOIN [Order Details] AS od ON o.OrderID = od.OrderID
WHERE o.OrderDate >= '1996-10-01'
AND o.orderdate < '1996-11-01' -- move up one day, use less than
GROUP BY
c.customerid
, c.companyname
, o.orderdate
ORDER BY
totalsales DESC
;
(od.unitprice * od.Quantity) provides total discounted price, then
the discount rate is (1 - od.Discount)
multiply those (od.unitprice * od.Quantity) * (1 - od.Discount) for total discounted price
Please note I have changed the syntax of the joins! PLEASE learn this more modern syntax. Don't use commas between table names in the from clause, then conditions such as AND o.customerid = c.CustomerID move to after ON instead of within the where clause..
Also, the most reliable date literals in SQL Server are yyyymmdd and the second best is yyyy-mm-dd. It's good to see you using year first, but I would suggest using dashes not slashes, or (even better) no delimiter. e.g.
WHERE o.OrderDate >= '19961001'
AND o.orderdate < '19961101'
Also note that I have removed the <= and replaced it with < and moved that higher date to the first of the next month. It is actually easier this way as every month has a day 1, just use less than this higher date.

Getting max value before given date

I am pretty new to using MS SQL 2012 and I am trying to create a query that will:
Report the order id, the order date and the employee id that processed the order
report the maximum shipping cost among the orders processed by the same employee prior to that order
This is the code that I've come up with, but it returns the freight of the particular order date. Whereas I am trying to get the maximum freight from all the orders before the particular order.
select o.employeeid, o.orderid, o.orderdate, t2.maxfreight
from orders o
inner join
(
select employeeid, orderdate, max(freight) as maxfreight
from orders
group by EmployeeID, OrderDate
) t2
on o.EmployeeID = t2.EmployeeID
inner join
(
select employeeid, max(orderdate) as mostRecentOrderDate
from Orders
group by EmployeeID
) t3
on t2.EmployeeID = t3.EmployeeID
where o.freight = t2.maxfreight and t2.orderdate < t3.mostRecentOrderDate
Step one is to read the order:
select o.employeeid, o.orderid, o.orderdate
from orders o
where o.orderid = #ParticularOrder;
That gives you everything you need to go out and get the previous orders from the same employee and join each one to the row you get from above.
select o.employeeid, o.orderid, o.orderdate, o2.freight
from orders o
join orders o2
on o2.employeeid = o.employeeid
and o2.orderdate < o.orderdate
where o.orderid = #ParticularOrder;
Now you have a whole bunch of rows with the first three values the same and the fourth is the freight cost of each previous order. So just group by the first three fields and select the maximum of the previous orders.
select o.employeeid, o.orderid, o.orderdate, max( o2.freight ) as maxfreight
from orders o
join orders o2
on o2.employeeid = o.employeeid
and o2.orderdate < o.orderdate
where o.orderid = #ParticularOrder
group by o.employeeid, o.orderid, o.orderdate;
Done. Build your query in stages and many times it will turn out to be much simpler than you at first thought.
It is unclear why you are using t3. From the question it doesn't sound like the employee's most recent order date is relevant at all, unless I am misunderstanding (which is absolutely possible).
I believe the issue lies in t2. You are grouping by orderdate, which will return the max freight for that date and employeeid, as you describe. You need to calculate a maximum total from all orders that occurred before the date that the order occurred on, for that employee, for every row you are returning.
It probably makes more sense to use a subquery for this.
SELECT o.employeeid, o.orderid, o.orderdate, m.maxfreight
FROM
orders o LEFT OUTER JOIN
(SELECT max(freight) as maxfreight
FROM orders AS f
WHERE f.orderdate <= o.orderdate AND f.employeeid = o.employeeid
) AS m
Hoping this is syntactically correct as I'm not in front of SSMS right now. I also included a left outer join as your previous query with an inner join would have excluded any rows where an employee had no previous orders (i.e. first order ever).
You can do what you want with a correlated subquery or apply. Here is one way:
select o.employeeid, o.orderid, o.orderdate, t2.maxfreight
from orders o outer apply
(select max(freight) as maxfreight
from orders o2
where o2.employeeid = o.employeid and
o2.orderdate < o.orderdate
) t2;
In SQL Server 2012+, you can also do this with a cumulative maximum:
select o.employeeid, o.orderid, o.orderdate,
max(freight) over (partition by employeeid
order by o.orderdate rows between unbounded preceding and 1 preceding
) as maxfreight
from orders o;

Filter by virtual column?

I have the following database structure :
[Order]
OrderId
Total
[Payment]
OrderId
Amount
Every Order can have X payment rows. I want to get only the list of orders where the sum of all the payments are < than the order Total.
I have the following SQL but I will return all the orders paid and unpaid.
SELECT o.OrderId,
o.UserId,
o.Total,
o.DateCreated,
COALESCE(SUM(p.Amount),0) AS Paid
FROM [Order] o
LEFT JOIN Payment p ON p.OrderId = o.OrderId
GROUP BY o.OrderId, o.Total, o.UserId, o.DateCreated
I have tried to add Where (Paid < o.Total) but it does not work, any idea?
BTM I'm using SQL CE 3.5
What you are looking for is the HAVING clause.
Instead of "Where (COALESCE(SUM(p.Amount),0) < o.Total)", try "HAVING (COALESCE(SUM(p.Amount),0) < o.Total)"
Check out the MSDN Reference on HAVING.
Select Order.* From Order
Left Join
(Select OrderID, Sum(Amount) As t From Payment Group By OrderID) As s On s.OrderID = Oder.OrderID
Where IsNULL(s.t,0) < Order.Total