How to get current and last order from Northwind db using Correlated queries - sql

Using the northwind db on mssql, i am trying to retrieve the customer's last two order dates and calculate the time between the two orders.
So something like
select c.CompanyName, o.OrderDate, o2.OrderDate,
DateDiff(d, o.OrderDate, o2.OrderDate) as TimeElapsed
unfortunately not sure how to construct it from there.
i have something like this but it's still wrong.
select c.CompanyName, o.OrderDate, o2.OrderDate,
DateDiff(d, o.OrderDate, o2.OrderDate) as TimeElapsed
from Orders o
INNER JOIN Customers ON c.CustomerID = o.CustomerID
INNER JOIN (
select OrderID, OrderDate
FROM Orders
order by OrderDate
OFFSET 1 ROWS
FETCH NEXT 1 ROW ONLY
) as o2 ON o.OrderID = o2.OrderID;
can anyone assist.
Thank you

Northwind has been obsolete for years; even AdventureWorks has been replaced. The following uses the latter but you should be able to easily translate it to your schema. Two different approaches. The last 2 select statements are used to verify the results. Notice that customer 30099 has only one order.
set nocount on;
with cte as (select SalesOrderID, OrderDate, CustomerID, row_number () over (partition by CustomerID order by OrderDate desc) as rn
from Sales.SalesOrderHeader)
select top 10 * from cte
where rn <= 2
order by CustomerID, rn;
with cte as (select SalesOrderID, OrderDate, CustomerID, row_number () over (partition by CustomerID order by OrderDate desc) as rn
from Sales.SalesOrderHeader)
select cte.CustomerID, min(cte.OrderDate) as mindate, max(cte.OrderDate),
case when min(cte.OrderDate) = max(cte.OrderDate) then cast(null as int)
else datediff(day, min(cte.OrderDate), max(cte.OrderDate)) end as dif
from cte
where rn <= 2
group by cte.CustomerID
order by CustomerID;
with cte as (select SalesOrderID, OrderDate, CustomerID, row_number () over (partition by CustomerID order by OrderDate desc) as rn
from Sales.SalesOrderHeader)
select cte.CustomerID, minr.OrderDate as mindate, cte.OrderDate as maxdate,
datediff(day, minr.OrderDate, cte.OrderDate) as dif
from cte left join cte as minr on cte.CustomerID = minr.CustomerID and minr.rn = 2
where cte.rn = 1
order by cte.CustomerID;
select top 2 CustomerID, OrderDate from Sales.SalesOrderHeader where CustomerID = 30118 order by OrderDate desc;
select top 2 CustomerID, OrderDate from Sales.SalesOrderHeader where CustomerID = 30099 order by OrderDate desc;

Related

SQL YTD and last year YTD on complete data

I need to calculate YTD and last year YTD on a table [SQL Server 2012]. Below is the query I tried. Its getting doubled and tripled for some cases.
SELECT SUM(A.RevisionNumber)YTD,SUM(P.RevisionNumber)LY_YTD,B.OrderDateM,B.OrderDateY
FROM
(select MONTH(OrderDate)OrderDateM,YEAR(OrderDate)OrderDateY from sales.SalesOrderHeader B
group by MONTH(OrderDate),YEAR(OrderDate))B
LEFT JOIN
(select SUM(RevisionNumber)RevisionNumber,MONTH(OrderDate)OrderDateM,YEAR(OrderDate)OrderDateY
from sales.SalesOrderHeader
group by MONTH(OrderDate),YEAR(OrderDate))A
ON A.OrderDateM<=B.OrderDateM AND A.OrderDateY=B.OrderDateY
LEFT JOIN
(select SUM(RevisionNumber)RevisionNumber,MONTH(OrderDate)OrderDateM,YEAR(OrderDate)OrderDateY
from sales.SalesOrderHeader
group by MONTH(OrderDate),YEAR(OrderDate))P
ON P.OrderDateM<=B.OrderDateM AND P.OrderDateY=B.OrderDateY-1
GROUP BY B.OrderDateM,B.OrderDateY
ORDER BY B.OrderDateY,B.OrderDateM
You can use windowing function as below:
;With cte as (
Select Sum(RevisionNumber) As SM_RevisionNumber, Month(OrderDate) as OrderM,
Year(OrderDate) as OrderY
From Sales.SalesOrderHeader
Group by Month(OrderDate), Year(OrderDate)
), cte2 as (
Select YTD = Sum(SM_RevisionNumber) over (partition by OrderY order by OrderM),
OrderM, OrderY, RowN = Row_Number() over(order by OrderY, OrderM)
from cte
)
Select YTD, LY_YTD = lag(YTD, 12, null) over(Order by RowN), OrderM, ORderY
from cte2
But this solution assumes we have atleast one entry for each month and year.

SQL Server 2008 calculating data difference when we have only one date column

I have a date column Order_date and I am looking for ways to calculate the date difference between customer last order date and his recent previous ( previous form last) order_date ....
Example
Customer : 1, 2 , 1 , 1
Order_date: 01/02/2007, 02/01/2015, 06/02/2014, 04/02/2015
As you can see customer # 1 has three orders.
I want to know the date difference between his recent order date (04/02/2015) and his recent previous (06/02/2014).
For SQL Server 2012 & 2014 you could use LAG with a DATEDIFF to see the number of days between them.
For older versions, a CTE would probably be your best bet:
;WITH CTE AS
(
SELECT CustomerID,
Order_Date,
rn = ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY Order_Date DESC)
)
SELECT c1.CustomerID,
DATEDIFF(d, c1.Order_Date, c2.Order_Date)
FROM CTE c1
INNER JOIN CTE c2 ON c2.rn = c1.rn + 1
In SQL Server 2012+, you can use lag() to get the difference between any two dates:
select t.*,
datediff(day, lag(order_date) over (partition by customer order by order_date),
order_date) as days_dff
from table t;
If you have an older version, you can do something similar with correlated subqueries or outer apply.
EDIT:
If you just want the difference between the two most recent dates, use conditional aggregation instead:
select customer,
datediff(day, max(case when seqnum = 2 then order_date end),
max(case when seqnum = 1 then order_date end)
) as MostRecentDiff
from (select t.*,
row_number() over (partition by customer order by order_date desc) as seqnum
from table t
) t
group by customer;
If you're using SQL Server 2008 or later, you can try CROSS APPLY.
SELECT [customers].[customer_id], DATEDIFF(DAY, MIN([recent_orders].[order_date]), MAX([recent_orders].[order_date])) AS [elapsed]
FROM [customers]
CROSS APPLY (
SELECT TOP 2 [order_date]
FROM [orders]
WHERE ([orders].[customer_id] = [customers].[customer_id])
) [recent_orders]
GROUP BY [customers].[customer_id]
SELECT DATEDIFF(DAY, Y.PrevLastOrderDate, Y.LastOrderDate) AS PreviousDays
FROM
(
SELECT X.LastOrderDate
, (SELECT MAX(OrderDate) FROM dbo.Orders SO WHERE SO.CustomerID=1 AND SO.OrderDate < X.LastOrderDate) AS PrevLastOrderDate
FROM
(
select MAX(OrderDate) AS LastOrderDate
FROM dbo.Orders O
WHERE O.CustomerID=1
)X
)Y
drop table #Invoices
create table #Invoices ( OrderId int , OrderDate datetime )
insert into #Invoices (OrderId , OrderDate )
select 101, '01/01/2001' UNION ALL Select 202, '02/02/2002' UNION ALL Select 303, '03/03/2003'
UNION ALL Select 808, '08/08/2008' UNION ALL Select 909, '09/09/2009'
;
WITH
MyCTE /* http://technet.microsoft.com/en-us/library/ms175972.aspx */
( OrderId,OrderDate,ROWID) AS
(
SELECT
OrderId,OrderDate
, ROW_NUMBER() OVER ( ORDER BY OrderDate ) as ROWID
FROM
#Invoices inv
)
SELECT
OrderId,OrderDate
,(Select Max(OrderDate) from MyCTE innerAlias where innerAlias.ROWID = (outerAlias.ROWID-1) ) as PreviousOrderDate
,
[MyDiff] =
CASE
WHEN (Select Max(OrderDate) from MyCTE innerAlias where innerAlias.ROWID = (outerAlias.ROWID-1) ) iS NULL then 0
ELSE DATEDIFF (mm, OrderDate , (Select Max(OrderDate) from MyCTE innerAlias where innerAlias.ROWID = (outerAlias.ROWID-1) ) )
END
, ROWIDMINUSONE = (ROWID-1)
, ROWID as ROWID_SHOWN_FOR_KICKS , OrderDate as OrderDateASecondTimeForConvenience
FROM
MyCTE outerAlias
ORDER BY outerAlias.OrderDate Desc , OrderId

How to pull the whole row which has the latest date in SQL?

I’m trying to select the pair of product – distribution center attached with the most recent order (based on order date). For one order I can have multiple products, but the whole order will be shipped from one specific distribution center.
How do I select the specific product-distribution center attached with the latest order?
My structure is basically like this:
data.orderdetail table has ordernum, orderdate, distributioncenter
I tried to pull like this, but it doesn’t give me the desired result. I’m using sql server 2008:
SELECT DISTINCT y.OrderNum, y.Product, y.DistributionCenter
, CAST(y.OrderDate AS DATE) AS Orderdate
FROM (SELECT OrderNum, MAX(CAST(Orderdate AS date)) AS orderdate
FROM data.OrderDetail
GROUP BY OrderNum) AS x
INNER JOIN data.OrderDetail AS y
ON y.OrderNum = x.OrderNum
It looks as if you need one more clause in your join Condition
You've got
ON y.OrderNum = x.OrderNum
Which will return all the orders that match the Order number in the subquery
But you'll need
ON y.OrderNum = x.OrderNum
AND y.OrderDate = x.orderdate
Which will return all the orders that match the Order number in the subquery and the maximum date for that order number
SELECT DISTINCT
y.OrderNum,
y.Product,
y.DistributionCenter,
CAST(y.OrderDate AS DATE) AS Orderdate
FROM (
SELECT
OrderNum,
MAX(CAST(Orderdate AS date)) AS orderdate
FROM data.OrderDetail
GROUP BY OrderNum
) AS x
INNER JOIN
data.OrderDetail AS y
ON y.OrderNum = x.OrderNum
AND y.OrderDate = x.orderdate
I believe what you are looking for is row_number. This will partition your result set by OrderNum then rank the sets by the OrderDate. You can then filter off the extra rows in another where clause.
select result.*,
CAST(result.OrderDate as date) as Orderdate,
from (
select y.*,
row_number() over (
partition by y.OrderNum order by CAST(y.OrderDate as date) desc
) rank_
from (
select OrderNum,
MAX(CAST(Orderdate as date)) as orderdate
from data.OrderDetail
group by OrderNum
) as x
inner join data.OrderDetail as y on y.OrderNum = x.OrderNum
) result
where result.rank_ = 1;
select * from
(
SELECT OrderNum, Product, DistributionCenter, OrderDate
, ROW_NUMBER() over (partition by OrderNum order by OrderDate desc) as rownum
FROM OrderDetail
) as xxx
where xxx.rownum = 1
ROW_NUMBER (Transact-SQL)
Try this.
; WITH CTE1
AS (
SELECT
od.OrderNum
, od.Product
, od.DistributionCenter
, CAST(od.OrderDate AS DATE) AS OrderDate
, RowNumber = ROW_NUMBER() OVER (PARTITION BY od.Product, od.DistributionCenter ORDER BY CAST(od.OrderDate AS DATE) DESC)
FROM data.OrderDetail od
)
SELECT
OrderNum
, Product
, DistributionCenter
, OrderDate
FROM CTE1
WHERE RowNumber = 1

TSQL- Finding the difference in days of multiple records in SQL Server

Is it possible to find the difference of days of different records in SQL Server 2008 R2?
SELECT OrderDate FROM OrdersTbl WHERE SKU='AA0000' ORDER BY ORDERDATE DESC
OrderDate
-----------------------
2009-12-03 00:00:00.000
2009-04-03 00:00:00.000
2008-02-22 00:00:00.000
2008-02-21 00:00:00.000
2007-02-18 00:00:00.000
2007-01-27 00:00:00.000
2006-10-13 00:00:00.000
I would like a way to get how many days in between there are for each order date so that I could find the average frequency. Thanks in advance.
You can do it with a common table expression and ROW_NUMBER:
WITH OrderDates AS (
SELECT
ROW_NUMBER() OVER (ORDER BY OrderDate DESC) AS RowNumber,
OrderDate
FROM OrdersTable
WHERE SKU = 'AA0000'
)
SELECT
AVG(DATEDIFF(DD, O2.OrderDate, O1.OrderDate)) AS AverageFrequency
FROM OrderDates O1
LEFT JOIN OrderDates O2
ON O2.RowNumber = O1.RowNumber + 1
Sucks there's no LEAD/LAG support in SQL Server:
SELECT z.orderdate,
z.prev_date,
DATEDIFF(dd, z.prev_date, z.orderdate)
FROM (SELECT OrderDate,
(SELECT MAX(y.orderdate)
FROM ORDERSTBL y
WHERE y.orderdate < x.orderdate
AND y.sku = x.sku) AS prev_date
FROM OrdersTbl x
WHERE x.sku ='AA0000') z
ORDER BY z.orderdate DESC
;With cteDifference as (
Select SKU, OrderDate, Row_Number() OVER (Partition by SKU Order by OrderDate) as RowNumber
from OrdersTbl
)
select cur.SKU,
cur.OrderDate as CurrentDate,
prev.OrderDate as PreviousDate,
DATEDIFF(DD,prev.OrderDate, cur.OrderDate) as DaysDifference
from cteDifference cur
left join cteDifference prev
on cur.SKU = prev.SKU
and cur.RowNumber = prev.RowNumber + 1
where cur.SKU = 'AA0000'
order by cur.OrderDate desc

MySQL: Returning multiple columns from an in-line subquery

I'm creating an SQL statement that will return a month by month summary on sales.
The summary will list some simple columns for the date, total number of sales and the total value of sales.
However, in addition to these columns, i'd like to include 3 more that will list the months best customer by amount spent. For these columns, I need some kind of inline subquery that can return their ID, Name and the Amount they spent.
My current effort uses an inline SELECT statement, however, from my knowledge on how to implement these, you can only return one column and row per in-line statement.
To get around this with my scenario, I can of course create 3 separate in-line statements, however, besides this seeming impractical, it increases the query time more that necessary.
SELECT
DATE_FORMAT(OrderDate,'%M %Y') AS OrderMonth,
COUNT(OrderID) AS TotalOrders,
SUM(OrderTotal) AS TotalAmount,
(SELECT SUM(OrderTotal) FROM Orders WHERE DATE_FORMAT(OrderDate,'%M %Y') = OrderMonth GROUP BY OrderCustomerFK ORDER BY SUM(OrderTotal) DESC LIMIT 1) AS TotalCustomerAmount,
(SELECT OrderCustomerFK FROM Orders WHERE DATE_FORMAT(OrderDate,'%M %Y') = OrderMonth GROUP BY OrderCustomerFK ORDER BY SUM(OrderTotal) DESC LIMIT 1) AS CustomerID,
(SELECT CustomerName FROM Orders INNER JOIN Customers ON OrderCustomerFK = CustomerID WHERE DATE_FORMAT(OrderDate,'%M %Y') = OrderMonth GROUP BY OrderCustomerFK ORDER BY SUM(OrderTotal) DESC LIMIT 1) AS CustomerName
FROM Orders
GROUP BY DATE_FORMAT(OrderDate,'%m%y')
ORDER BY DATE_FORMAT(OrderDate,'%y%m') DESC
How can i better structure this query?
FULL ANSWER
After some tweaking of Dave Barkers solution, I have a final version for anyone in the future looking for help.
The solution by Dave Barker worked perfectly with the customer details, however, it made the simpler Total Sales and Total Sale Amount columns get some crazy figures.
SELECT
Y.OrderMonth, Y.TotalOrders, Y.TotalAmount,
Z.OrdCustFK, Z.CustCompany, Z.CustOrdTotal, Z.CustSalesTotal
FROM
(SELECT
OrdDate,
DATE_FORMAT(OrdDate,'%M %Y') AS OrderMonth,
COUNT(OrderID) AS TotalOrders,
SUM(OrdGrandTotal) AS TotalAmount
FROM Orders
WHERE OrdConfirmed = 1
GROUP BY DATE_FORMAT(OrdDate,'%m%y')
ORDER BY DATE_FORMAT(OrdDate,'%Y%m') DESC)
Y INNER JOIN
(SELECT
DATE_FORMAT(OrdDate,'%M %Y') AS CustMonth,
OrdCustFK,
CustCompany,
COUNT(OrderID) AS CustOrdTotal,
SUM(OrdGrandTotal) AS CustSalesTotal
FROM Orders INNER JOIN CustomerDetails ON OrdCustFK = CustomerID
WHERE OrdConfirmed = 1
GROUP BY DATE_FORMAT(OrdDate,'%m%y'), OrdCustFK
ORDER BY SUM(OrdGrandTotal) DESC)
Z ON Z.CustMonth = Y.OrderMonth
GROUP BY DATE_FORMAT(OrdDate,'%Y%m')
ORDER BY DATE_FORMAT(OrdDate,'%Y%m') DESC
Move the inline SQL to be a inner join query. So you'd have something like...
SELECT DATE_FORMAT(OrderDate,'%M %Y') AS OrderMonth, COUNT(OrderID) AS TotalOrders, SUM(OrderTotal) AS TotalAmount, Z.OrderCustomerFK, Z.CustomerName, z.OrderTotal as CustomerTotal
FROM Orders
INNER JOIN (SELECT DATE_FORMAT(OrderDate,'%M %Y') as Mon, OrderCustomerFK, CustomerName, SUM(OrderTotal) as OrderTotal
FROM Orders
GROUP BY DATE_FORMAT(OrderDate,'%M %Y'), OrderCustomerFK, CustomerName ORDER BY SUM(OrderTotal) DESC LIMIT 1) Z
ON Z.Mon = DATE_FORMAT(OrderDate,'%M %Y')
GROUP BY DATE_FORMAT(OrderDate,'%m%y'), Z.OrderCustomerFK, Z.CustomerName
ORDER BY DATE_FORMAT(OrderDate,'%y%m') DESC
You can also do something like:
SELECT
a.`y`,
( SELECT #c:=NULL ) AS `temp`,
( SELECT #d:=NULL ) AS `temp`,
( SELECT
CONCAT(#c:=b.`c`, #d:=b.`d`)
FROM `b`
ORDER BY b.`uid`
LIMIT 1 ) AS `temp`,
#c as c,
#d as d
FROM `a`
Give this a shot:
SELECT CONCAT(o.order_month, ' ', o.order_year),
o.total_orders,
o.total_amount,
x.sum_order_total,
x.ordercustomerfk,
x.customername
FROM (SELECT MONTH(t.orderdate) AS order_month,
YEAR(t.orderdate) AS order_year
COUNT(t.orderid) AS total_orders,
SUM(t.ordertotal) AS total_amount
FROM ORDERS t
GROUP BY MONTH(t.orderdate), YEAR(t.orderdate)) o
JOIN (SELECT MONTH(t.orderdate) AS ordermonth,
YEAR(t.orderdate) AS orderyear
SUM(t.ordertotal) 'sum_order_total',
t.ordercustomerfk,
c.customername
FROM ORDERS t
JOIN CUSTOMERS c ON c.customerid = o.ordercustomerfk
GROUP BY t.ordercustomerfk, MONTH(t.orderdate), YEAR(t.orderdate)) x ON x.order_month = o.order_month
AND x.order_year = o.order_year
ORDER BY o.order_year DESC, o.order_month DESC