How to calculate date difference between different visits using SQL - sql

I want to calculate the day difference between first visit and the second visit; second visit and third visits etc. per customer using SQL. Please assist.
For example, Customer A visited three times on
2016-01-03, 2016-01-06 and 2016-05-30 while customer B visited ten times with different dates.
Query
With cte as (Select customerid, VisitDate,
ROW_NUMBER() OVER(PARTITION BY CustomerID ORDER BY VisitDate) as rownum
FROM visitTable V)
Select CustomerID, VisitDate, rownum, DateDiff(D,R1.VisitDate, R2.VisitDate) as NoOfDays
FROM cte R1
LEFT JOIN cte R2 ON R1.CustomerID = R2.CustomerID AND R1.rownum = 1 AND R2.rownum = 2
Thank you

I think you were pretty close to the right idea. Your join needs to compare row numbers. I also switched the order of your date diff but I didn't test it.
With cte as (
Select customerid, VisitDate,
ROW_NUMBER() OVER(PARTITION BY CustomerID ORDER BY VisitDate) as rownum
FROM visitTable V
)
Select R1.CustomerID, R1.VisitDate, R1.rownum,
DateDiff(D,R2.VisitDate, R1.VisitDate) as NoOfDays --this is days since last visit
FROM cte R1 --current row
LEFT JOIN cte R2 --previous visit - will result in null days for 1st row.
ON R1.CustomerID = R2.CustomerID
AND R1.rownum - 1 = R2.rownum
order by R1.CustomerID, R1.VisitDate;

Related

Calculate average days between orders The last three records tsql

I trying to take an average per customer, but you're not grouping by customer.
I would like to calculate the average days between several order dates from a table called invoice. For each BusinessPartnerID, what is the average days between orders i want average days last three records orders .
I got the average of all order for each user but need days last three records orders
The sample table is as below
;WITH temp (avg,invoiceid,carname,carid,fullname,mobail)
AS
(
SELECT AvgLag = AVG(Lag) , Lagged.idinvoice,
Lagged.carname ,
Lagged.carid ,Lagged.fullname,Lagged.mobail
FROM
(
SELECT
(car2.Name) as carname ,
(car2.id) as carid ,( busin.Name) as fullname, ( busin.Mobile) as mobail , INV.Id as idinvoice , Lag = CONVERT(int, DATEDIFF(DAY, LAG(Date,1)
OVER (PARTITION BY car2.Id ORDER BY Date ), Date))
FROM [dbo].[Invoice] AS INV
JOIN [dbo].[InvoiceItem] AS INITEM on INV.Id=INITEM.Invoiceid
JOIN [dbo].[BusinessPartner] as busin on busin.Id=INV.BuyerId and Type=5
JOIN [dbo].[Product] as pt on pt.Id=INITEM.ProductId and INITEM.ProductId is not null and pt.ProductTypeId=3
JOIN [dbo].[Car] as car2 on car2.id=INv.BusinessPartnerCarId
) AS Lagged
GROUP BY
Lagged.carname,
Lagged.carid,Lagged.fullname,Lagged.mobail, Lagged.idinvoice
-- order by Lagged.fullname
)
SELECT * FROM temp where avg is not null order by avg
I don't really see how your query relate to your question. Starting from a table called invoice that has columns businesspartnerid, and date, here is how you would take the average of the day difference between the last 3 invoices of each business partner:
select businesspartnerid,
avg(1.0 * datediff(
day,
lag(date) over(partition by businesspartnerid order by date),
date
) avg_diff_day
from (
select i.*,
row_number() over(partiton by businesspartnerid order by date desc) rn
from invoice i
) i
where rn <= 3
group by businesspartnerid
Note that 3 rows gives you 2 intervals only, that will be averaged.

SQL: Difference between consecutive rows

Table with 3 columns: order id, member id, order date
Need to pull the distribution of orders broken down by No. of days b/w 2 consecutive orders by member id
What I have is this:
SELECT
a1.member_id,
count(distinct a1.order_id) as num_orders,
a1.order_date,
DATEDIFF(DAY, a1.order_date, a2.order_date) as days_since_last_order
from orders as a1
inner join orders as a2
on a2.member_id = a1.member_id+1;
It's not helping me completely as the output I need is:
You can use lag() to get the date of the previous order by the same customer:
select o.*,
datediff(
order_date,
lag(order_date) over(partition by member_id order by order_date, order_id)
) days_diff
from orders o
When there are two rows for the same date, the smallest order_id is considered first. Also note that I fixed your datediff() syntax: in Hive, the function just takes two dates, and no unit.
I just don't get the logic you want to compute num_orders.
May be something like this:
SELECT
a1.member_id,
count(distinct a1.order_id) as num_orders,
a1.order_date,
DATEDIFF(DAY, a1.order_date, a2.order_date) as days_since_last_order
from orders as a1
inner join orders as a2
on a2.member_id = a1.member_id
where not exists (
select intermediate_order
from orders as intermedite_order
where intermediate_order.order_date < a1.order_date and intermediate_order.order_date > a2.order_date) ;

How do I get all rows from the second to latest date?

I have gotten all rows for the latest date like this:
SELECT date, quarter, sales_region, revenue
FROM regions
WHERE date = (SELECT MAX(date) FROM regions)
ORDER BY 1
So how would I get the rows for the second latest date?
I have tried but no luck:
SELECT MAX(date), quarter, sales_region, revenue
FROM regions
WHERE date < (SELECT MAX(date) FROM regions)
ORDER BY 1
Here is one method:
SELECT date, quarter, sales_region, revenue
FROM regions
WHERE date = (SELECT DISTINCT date
FROM regions r2
ORDER BY date DESC
OFFSET 1 FETCH FIRST 1 ROW ONLY
)
ORDER BY 1;
Another method uses dense_rank():
select r.*
from (select r.*, dense_rank() over (order by date desc) as seqnum
from regions r
) r
where seqnum = 2;
Gordon answered your question precisely, but if you want to get the records for the last two dates in one query, you could use IN instead of =, and get the top two records with LIMIT 2:
SELECT date, quarter, sales_region, revenue
FROM regions
WHERE date IN (SELECT DISTINCT date
FROM regions r2
ORDER BY date DESC
LIMIT 2)
ORDER BY 1;
Starting with version 8.4, you can also use FETCH FIRST 2 ROW ONLY instead of LIMIT 2.

Find date ranges between large gaps and ignore smaller gaps

I have a column of a mostly continous unique dates in ascending order. Although the dates are mostly continuos, there are some gaps in the dates of less than 3 days, others have more than 3 days.
I need to create a table where each record has a start date and an end date of the range that includes a gap of 3 days or less. But a new record has to be generated if the gap is longer than 3 days.
so if dates are:
1/2/2012
1/3/2012
1/4/2012
1/15/2012
1/16/2012
1/18/2012
1/19/2012
I need:
1/2/2012 1/4/2012
1/15/2012 1/19/2012
You can do something like this:
WITH CTE_Source AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY DT) RN
FROM dbo.Table1
)
,CTE_Recursion AS
(
SELECT *, 1 AS Grp
FROM CTE_Source
WHERE RN = 1
UNION ALL
SELECT src.*, CASE WHEN DATEADD(DD,3,rec.DT) < src.DT THEN rec.Grp + 1 ELSE Grp END AS Grp
FROM CTE_Source src
INNER JOIN CTE_Recursion rec ON src.RN = rec.RN +1
)
SELECT
MIN(DT) AS StartDT, MAX(DT) AS EndDT
FROM CTE_Recursion
GROUP BY Grp
First CTE is just to assign continuous numbers for all rows in order to join them later. Then using recursive CTE you can join on each next row assigning groups if date difference is larger than 3 days. In the end just group by grouping column and select desired results.
SQLFiddle DEMO

SQL Count Of Open Orders Each Day Between Two Dates

I've tried searching but it's likely I'm using the wrong keywords as I can't find an answer.
I'm trying to find the number of orders that are open between two dates and by employee. I have one table that shows a list of employees, another that shows a list of orders that contains an open and close date and also a dates table if that helps.
The employee and order tables joined will return something like:
employee order ref opened closed
a 123 01/01/2012 04/01/2012
b 124 02/01/2012 03/01/2012
a 125 02/01/2012 03/01/2012
And I need to transform this data into:
Date employee Count
01/01/2012 a 1
02/01/2012 a 2
02/01/2012 b 1
03/01/2012 a 2
03/01/2012 b 1
04/01/2012 a 1
I'm pulling the data from SQL server.
Any ideas?
Thanks
Nick
Join Dates to the result of the join between Employees and Orders, then group by dates and employees to obtain the counts, something like this:
SELECT
d.Date,
o.Employee,
COUNT(*) AS count
FROM Employees e
INNER JOIN Orders o ON e.ID = o.Employee
INNER JOIN Dates d ON d.Date BETWEEN o.Opened AND o.Closed
GROUP BY
d.Date,
o.Employee
My favorite way to do this counts the number of cumulative opens and the number of cumulative closes over time.
with cumopens as
(select employee, opened as thedate,
row_number() over (partition by employee order by opened) as cumopens,
0 as cumcloses
from eo
),
cumcloses as
(select employee, closed as thedate, 0 as cumopens,
row_number() over (partition by employee order by closed ) as cumcloses
from eo
)
select employee, c.thedate, max(cumopens), max(cumcloses),
max(cumopens) - max(cumcloses) as stillopened
from ((select *
from cumopens
) union all
(select *
from cumcloses
)
) c
group by employee, thedate
The only problem with this approach is that only dates where there is employee activity get reported. This works in your case.
The more general solution requires a sequence numbers to generate dates. For this, I often create one from some existing table with enough rows:
with nums as
(select row_number() over (partition by null order by null) as seqnum
from employees
)
select employee, dateadd(day, opened, seqnum) as thedate, count(*)
from eo join
nums
on datediff(day, opened, closed) < seqnum
group by employee, dateadd(day, opened, seqnum)
order by 1, 2
SELECT opened,employee,count(*)
FROM employee LEFT JOIN orders
WHERE opened < firstDate and opened > secondDate
GROUP BY opened,employee
or you can change the first condition in
WHERE opened BETWEEN firstDate and secondDate
Calling the result column count was a bit odd because it seems to be in fact a row number.
You can do that by using ROW_NUMBER.
The other interesting part is that you also want open date and close date as separate rows. Using a simple UNION will solve that.
WITH cte
AS (SELECT Row_number() OVER ( PARTITION BY employee
ORDER BY order_ref) count,
employee,
opened,
closed
FROM orders)
SELECT employee, opened date, count
FROM cte
UNION ALL
SELECT employee, closed date, count
FROM cte
ORDER BY Date,
employee
DEMO