T-SQL recursive in order to find transactions for a period of time - sql

I have tried but i am not able to figure this out. I have a table transactions (transaction_ID, transaction_Person_ID, Transaction_Date etc).
What i want is to return all the transaction_person_ID's that have more than 3 transactions per week for the last year. That means i have to check for 1-1-10 to 7-1-10 to see if someone had more than 3 transactions for that 7 day period, then for 2-1-10 to 8-1-10 then 3-1-10 to 9-1-10 etc etc.
I now i need to use a recursive select but i what i have writen does not produce the correct time frame.
What i have written so far is this
WITH Dates AS (
SELECT
[Date] = CONVERT(DATETIME,'01/01/2010')
UNION ALL SELECT
[Date] = DATEADD(DAY, 1, [Date])
FROM
Dates
WHERE
Date < '12/31/2010'
)
SELECT transaction_person_Id FROM transactions
JOIN DATES
ON transactions.transaction_date = dates.date
where transactions.Transaction_Date between dateadd(DAYOFYEAR,-7,dates.date) and dates.date
group by transaction_person_Id
having count(transaction_person_ID) >= 4
OPTION (MAXRECURSION 2000)
Thanks a lot
PS:
in simple words what i need to do is this
select transaction_person_ID from transactions
where Transaction_Date between '2010-01-01' and '2010-01-07'
group by transaction_person_Id
having count(transaction_person_ID) >= 4
then
select transaction_person_ID from transactions
where Transaction_Date between '2010-01-02' and '2010-01-08'
group by transaction_person_Id
having count(transaction_person_ID) >= 4
.
.
.
.
.
until it goes
select transaction_person_ID from transactions
where Transaction_Date between '2010-12-25' and '2010-12-31'
group by transaction_person_Id
having count(transaction_person_ID) >= 4
i need to have the results of these 365 queries

This will give one result set with person and weeks, rather then 360 results sets
WITH Weeks
AS (
SELECT
CONVERT(DATETIME,'01 Jan 2010') AS WeekStartMidnight,
CONVERT(DATETIME,'08 Jan 2010') AS WeekEndMidnight
UNION ALL
SELECT
DATEADD(day, 1, WeekStartMidnight),
DATEADD(day, 1, WeekEndMidnight)
FROM
Weeks
WHERE
WeekEndMidnight < '31 Dec 2010'
)
SELECT
t.transaction_person_Id,
w.WeekStartMidnight, w.WeekEndMidnight
FROM
weeks w
JOIN
transactions t ON t.Transaction_Date >= w.WeekStartMidnight AND t.Transaction_Date < w.WeekEndMidnight
GROUP BY
t.transaction_person_Id
HAVING
count(*) >= 4 --COUNT(t.transaction_person_Id) = same
OPTION
(MAXRECURSION 365)
If you want 360 results sets, it's a loop using WHILE or a CURSOR per row in the "weeks" derived table

Related

Customers who placed order both in this month and previous month for a list of dates

So I am trying to find count of customers who placed order both in this month and previous month. I have to find this from the beginning of last year. I came up with a query which obviously doesn't work. Can I get some help with this please?
Query:
SELECT DATE_TRUNC('month', month_column), COUNT(DISTINCT(customer_id))
FROM table
WHERE month_column >= '2021-01-01' AND customer_id IN (
SELECT customer_id
FROM table
WHERE month_column = month_column - INTERVAL '1 month')
GROUP BY 1
NOTE: month_column has only month number i.e., '2021-01-01', '2021-02-01' etc.
I am using postgresql.
This is my first stack overflow question. So, if I didn't abide by any rules, I apologize.
To make this trivial, you can use 2 queries. Get customerID's from this month (insert into temp table 1) and customerID's from last month (insert into temp table 2). Lastly just inner join both tables on customerID
something like the below
SELECT customer_id
INTO #thisMonth
FROM customer
WHERE month_column > DATEADD(month, 0, GETDATE())
SELECT customer_id
INTO #prevMonth
FROM customer
WHERE month_column > DATEADD(month, -1, GETDATE())
SELECT COUNT(customer_id)
FROM #thisMonth tm
INNERJOIN #prevMonth pm ON tm.customer_id = pm.customerID

Finding Active Clients By Date

I'm having trouble writing a recursive function that would count the number of active clients on any given day.
Say I have a table like this:
Client
Start Date
End Date
1
1-Jan-22
2
1-Jan-22
3-Jan-22
3
3-Jan-22
4
4-Jan-22
5-Jan-22
5
4-Jan-22
6-Jan-22
6
7-Jan-22
9-Jan-22
I want to return a table that would look like this:
Date
NumActive
1-Jan-22
2
2-Jan-22
2
3-Jan-22
3
4-Jan-22
4
5-Jan-22
4
6-Jan-22
3
7-Jan-22
3
8-Jan-22
3
9-Jan-22
4
Is there a way to do this? Ideally, I'd have a fixed start date and go to today's date.
Some pieces I have tried:
Creating a recursive date table
Truncated to Feb 1, 2022 for simplicity:
WITH DateDiffs AS (
SELECT DATEDIFF(DAY, '2022-02-02', GETDATE()) AS NumDays
)
, Numbers(Numbers) AS (
SELECT MAX(NumDays) FROM DateDiffs
UNION ALL
SELECT Numbers-1 FROM Numbers WHERE Numbers > 0
)
, Dates AS (
SELECT
Numbers
, DATEADD(DAY, -Numbers, CAST(GETDATE() -1 AS DATE)) AS [Date]
FROM Numbers
)
I would like to be able to loop over the dates in that table, such as by modifying the query below for each date, such as by #loopdate. Then UNION ALL it to a larger final query.
I'm now stuck as to how I can run the query to count the number of active users:
SELECT
COUNT(Client)
FROM clients
WHERE [Start Date] >= #loopdate AND ([End Date] <= #loopdate OR [End Date] IS NULL)
Thank you!
You don't need anything recursive in this particular case, you need as a minimum a list of dates in the range you want to report on, ideally a permanent calendar table.
for purposes of demonstration you can create something on the fly, and use it like so, with the list of dates something you outer join to:
with dates as (
select top(9)
Convert(date,DateAdd(day, -1 + Row_Number() over(order by (select null)), '20220101')) dt
from master.dbo.spt_values
)
select d.dt [Date], c.NumActive
from dates d
outer apply (
select Count(*) NumActive
from t
where d.dt >= t.StartDate and (d.dt <= t.EndDate or t.EndDate is null)
)c
See this Demo Fiddle

how to find count of active sellers from transaction table

from the table given below find the count of active sellers for each day in June 2017. A seller is considered active when he has listed at least one item in last 30 days from the transaction date.
sample table:
Well, one method is to generate the dates and use a lateral join to calculate the number of sellers:
with dates as (
select cast('2017-06-01' as date) as dte
union all
select dateadd(day, 1, dte)
from dates
where dte < '2017-06-30'
)
select d.dte, t.num_sellers
from dates d outer apply
(select count(distinct t.seller_id) as num_sellers
from t
where t.transaction_date > dateadd(day, -30, d.dte) and
t.transaction_date <= d.dte
) t
order by d.dte;

How To Select Records in a Status Between Timestamps? T-SQL

I have a T-SQL Quotes table and need to be able to count how many quotes were in an open status during past months.
The dates I have to work with are an 'Add_Date' timestamp and an 'Update_Date' timestamp. Once a quote is put into a 'Closed_Status' of '1' it can no longer be updated. Therefore, the 'Update_Date' effectively becomes the Closed_Status timestamp.
I'm stuck because I can't figure out how to select all open quotes that were open in a particular month.
Here's a few example records:
Quote_No Add_Date Update_Date Open_Status Closed_Status
001 01-01-2016 NULL 1 0
002 01-01-2016 3-1-2016 0 1
003 01-01-2016 4-1-2016 0 1
The desired result would be:
Year Month Open_Quote_Count
2016 01 3
2016 02 3
2016 03 2
2016 04 1
I've hit a mental wall on this one, I've tried to do some case when filtering but I just can't seem to figure this puzzle out. Ideally I wouldn't be hard-coding in dates because this spans years and I don't want to maintain this once written.
Thank you in advance for your help.
You are doing this by month. So, three options come to mind:
A list of all months using left join.
A recursive CTE.
A number table.
Let me show the last:
with n as (
select row_number() over (order by (select null)) - 1 as n
from master..spt_values
)
select format(dateadd(month, n.n, q.add_date), 'yyyy-MM') as yyyymm,
count(*) as Open_Quote_Count
from quotes q join
n
on (closed_status = 1 and dateadd(month, n.n, q.add_date) <= q.update_date) or
(closed_status = 0 and dateadd(month, n.n, q.add_date) <= getdate())
group by format(dateadd(month, n.n, q.add_date), 'yyyy-MM')
order by yyyymm;
This does assume that each month has at least one open record. That seems reasonable for this purpose.
You can use datepart to extract parts of a date, so something like:
select datepart(year, add_date) as 'year',
datepart(month, date_date) as 'month',
count(1)
from theTable
where open_status = 1
group by datepart(year, add_date), datepart(month, date_date)
Note: this counts for the starting month and primarily to show the use of datepart.
Updated as misunderstood the initial request.
Consider following test data:
DECLARE #test TABLE
(
Quote_No VARCHAR(3),
Add_Date DATE,
Update_Date DATE,
Open_Status INT,
Closed_Status INT
)
INSERT INTO #test (Quote_No, Add_Date, Update_Date, Open_Status, Closed_Status)
VALUES ('001', '20160101', NULL, 1, 0)
, ('002', '20160101', '20160301', 0, 1)
, ('003', '20160101', '20160401', 0, 1)
Here is a recursive solution, that doesn't rely on system tables BUT also performs poorer. As we are talking about months and year combinations, the number of recursions will not get overhand.
;WITH YearMonths AS
(
SELECT YEAR(MIN(Add_Date)) AS [Year]
, MONTH(MIN(Add_Date)) AS [Month]
, MIN(Add_Date) AS YMDate
FROM #test
UNION ALL
SELECT YEAR(DATEADD(MONTH,1,YMDate))
, MONTH(DATEADD(MONTH,1,YMDate))
, DATEADD(MONTH,1,YMDate)
FROM YearMonths
WHERE YMDate <= SYSDATETIME()
)
SELECT [Year]
, [Month]
, COUNT(*) AS Open_Quote_Count
FROM YearMonths ym
INNER JOIN #test t
ON (
[Year] * 100 + [Month] <= CAST(FORMAT(t.Update_Date, 'yyyyMM') AS INT)
AND t.Closed_Status = 1
)
OR (
[Year] * 100 + [Month] <= CAST(FORMAT(SYSDATETIME(), 'yyyyMM') AS INT)
AND t.Closed_Status = 0
)
GROUP BY [Year], [Month]
ORDER BY [Year], [Month]
Statement is longer, also more readable and lists all year/month combinations to date.
Take a look at Date and Time Data Types and Functions for SQL-Server 2008+
and Recursive Queries Using Common Table Expressions

Query to check number of records created in a month.

My table creates a new record with timestamp daily when an integration is successful. I am trying to create a query that would check (preferably automated) the number of days in a month vs number of records in the table within a time frame.
For example, January has 31 days, so i would like to know how many days in january my process was not successful. If the number of records is less than 31, than i know the job failed 31 - x times.
I tried the following but was not getting very far:
SELECT COUNT (DISTINCT CompleteDate)
FROM table
WHERE CompleteDate BETWEEN '01/01/2015' AND '01/31/2015'
Every 7 days the system executes the job twice, so i get two records on the same day, but i am trying to determine the number of days that nothing happened (failures), so i assume some truncation of the date field is needed?!
One way to do this is to use a calendar/date table as the main source of dates in the range and left join with that and count the number of null values.
In absence of a proper date table you can generate a range of dates using a number sequence like the one found in the master..spt_values table:
select count(*) failed
from (
select dateadd(day, number, '2015-01-01') date
from master..spt_values where type='P' and number < 365
) a
left join your_table b on a.date = b.CompleteDate
where b.CompleteDate is null
and a.date BETWEEN '01/01/2015' AND '01/31/2015'
Sample SQL Fiddle (with count grouped by month)
Assuming you have an Integers table*. This query will pull all dates where no record is found in the target table:
declare #StartDate datetime = '01/01/2013',
#EndDate datetime = '12/31/2013'
;with d as (
select *, date = dateadd(d, i - 1 , #StartDate)
from dbo.Integers
where i <= datediff(d, #StartDate, #EndDate) + 1
)
select d.date
from d
where not exists (
select 1 from <target> t
where DATEADD(dd, DATEDIFF(dd, 0, t.<timestamp>), 0) = DATEADD(dd, DATEDIFF(dd, 0, d.date), 0)
)
Between is not safe here
SELECT 31 - count(distinct(convert(date, CompleteDate)))
FROM table
WHERE CompleteDate >= '01/01/2015' AND CompleteDate < '02/01/2015'
You can use the following query:
SELECT DATEDIFF(day, t.d, dateadd(month, 1, t.d)) - COUNT(DISTINCT CompleteDate)
FROM mytable
CROSS APPLY (SELECT CAST(YEAR(CompleteDate) AS VARCHAR(4)) +
RIGHT('0' + CAST(MONTH(CompleteDate) AS VARCHAR(2)), 2) +
'01') t(d)
GROUP BY t.d
SQL Fiddle Demo
Explanation:
The value CROSS APPLY-ied, i.e. t.d, is the ANSI string of the first day of the month of CompleteDate, e.g. '20150101' for 12/01/2015, or 18/01/2015.
DATEDIFF uses the above mentioned value, i.e. t.d, in order to calculate the number of days of the month that CompleteDate belongs to.
GROUP BY essentially groups by (Year, Month), hence COUNT(DISTINCT CompleteDate) returns the number of distinct records per month.
The values returned by the query are the differences of [2] - 1, i.e. the number of failures per month, for each (Year, Month) of your initial data.
If you want to query a specific Year, Month then just simply add a WHERE clause to the above:
WHERE YEAR(CompleteDate) = 2015 AND MONTH(CompleteDate) = 1