Recurring events reminder in SQL - sql

I'm trying to run a simple query to calculate what will be a reminder based upon one date (enrollment_date.) It's a recurring event every 90 days starting with the enrollment date but our staff needs only to see the next possible date. So if one date is in December they will only see that date until it passes and then only see the next after that date has passed. Is that possible? I'm using oracle sql
All that will be seen is:
Until December 12 2015
Client Next Inquiry Date
Client A December 12, 2015
After December 12 2015
Client Next Inquiry Date
Client A March 11 2016
It's not much but this is what I have so far.
SELECT
client_name, enrollment_date,
CASE
WHEN CURRENT_DATE <= DATE(day, 90, nextdue_date)

I think there are still quite a few unknowns with your scenario, but here is an example (for SQL Server) that will show you the next 100 90-day reminders. You can use a CTE to generate a list of reminder dates for each client then use the MIN from that list for each client that is still greater than the current date.
DECLARE #enroll_date DATE = '2015-12-12' --For testing
--DECLARE #enroll_reminder DATE = DATEADD(DAY, 90, #enroll_date)
;WITH cte AS (
SELECT 1 AS count_
UNION ALL
SELECT count_+1
FROM cte
WHERE count_<100
)
SELECT
'Client A'--replace with your column name
,MIN(DATEADD(DAY, 90*count_, #enroll_date)) AS test
FROM cte
--GROUP BY your_column_name
HAVING MIN(DATEADD(DAY, 90*count_, #enroll_date)) > GETDATE()
In this scenario, the next reminder is 2016-03-11, then when that day passes it will show 2016-06-09, then 2016-09-07, etc.

Check if this works for you...
Since this is Oracle (PL/SQL) we cannot use TOP 1 and hence using ROWNUM property.
SELECT client_name as "Client"
, enrollment_date as "Next Inquiry Date"
FROM <table_name>
WHERE enrollment_date < (SYSDATE + 90)
AND ROWNUM = 1
ORDER BY enrollment_date

Related

Generate custom start and end of months in SQL Server

I'm facing an issue while working with custom dates in T-SQL, we have a client that works with a different methodology of start and end of his month, instead of the default day 01 to start the month and ending in 31, 30 or 29, it's month start at day 26 and ends at 25 of the next month.
E.g., how usually is:
select sum(sales) as sales
from sales
where salesDate between '2020-09-01' and '2020-09-30'
-- or left(salesDate,7) = '2020-09'
Client custom month:
select sum(sales) as sales
from sales
where salesDate between '2020-08-26' and '2020-09-25' -- for setember
So, for this need, I have to calculate how many sales this client did from january until now, month per month, with this custom way... how can I do that?
Example of the query result I want to perform with this month determination:
This is a pretty awful situation. One method is to construct the first date of the month based on the rules:
select mon, sum(sales) as sales
from sales s cross apply
(values (case when day(salesdate) >= 26
then dateadd(month, 1, datefromparts(year(salesdate), month(salesdate), 1))
else datefromparts(year(salesdate), month(salesdate), 1)
) v(mon)
where v.mon >= '2020-01-01'
group by v.mon;
I would recommend adding the fiscal month column as a persisted computed column in the salesDate table so you can add an index and not have to worry about the computation.
Or, better yet, add a calendar table where you can look up the fiscal month for any (reasonable) date.

I want to get a specific answer by comparing two columns in postgresql

I have a query like this :
with base_data as
( Select
receipt_date,
receipt_value,
receipt_customer_id
From table1 )
Select
count(distinct (receipt_customer_id) , sum(receipt_value)
From
base_data
where
(receipt_date:timestamp <= current_date - interval '1 month' and
receipt_date: timestamp >= current_date - interval '2 month)
This basically gives me the number of distinct clients and their sum of receipt values for July and August considering the current month as September
I want to reduce this further and just want data for
distinct clients and sum of their receipt values
for whom there was no receipt in July i.e. they never transacted with us in July but came back in August basically they skipped a month and then transacted again.
I am unable to write this clause which I am putting in English below as a problem statement :
Give me the data for a distinct count of clients and their total sum of receipts who transacted with us in August but had no receipt value in July
I hope I am able to explain it. I have been racking my brain on this for a while but am unable to figure out a solution. Please help.
The current result looks like this
Count: 120
Sum: 207689
I want it reduced to (assumption)
Count: 12
Sum: 7000
The first issue I can see is with "sum of receipt values for July and August"; the return from your current query will depend upon when it is run (and will not be for calendar months). Lets put that aside and simplify/fix (the query as stated does not run) your query to one that will list all transactions in August (I think its simpler to understand using hard coded dates for now):
Select
receipt_customer_id, sum(receipt_value)
From
table1
where
-- Transacted in August
receipt_date >= '2020-08-01'::timestamp and
receipt_date < '2020-09-01'::timestamp
group by receipt_customer_id;
We can now add another clause to the where to filter out customers with transactions totalling $0/NULL (so total of $0 or no transactions at all) in July:
Select
receipt_customer_id, sum(receipt_value)
From
table1 t
where
-- Transacted in August
t.receipt_date >= '2020-08-01'::timestamp and
t.receipt_date < '2020-09-01'::timestamp
and (
select coalesce(sum(receipt_value), 0)
from table1
where
receipt_customer_id = t.receipt_customer_id and
-- Transacted in July
receipt_date >= '2020-07-01'::timestamp and
receipt_date < '2020-08-01'::timestamp
) = 0
group by receipt_customer_id;
or if you just want the count of customers and sum of receipt_value:
Select
count(distinct receipt_customer_id), sum(receipt_value)
From
table1 t
where
-- Transacted in August
t.receipt_date >= '2020-08-01'::timestamp and
t.receipt_date < '2020-09-01'::timestamp
and (
select coalesce(sum(receipt_value), 0)
from table1
where
receipt_customer_id = t.receipt_customer_id and
-- Transacted in July
receipt_date >= '2020-07-01'::timestamp and
receipt_date < '2020-08-01'::timestamp
) = 0
See this db fiddle for a test of this (feel free to use this if you want to ask follow-up questions). Note that if you want to reintroduce current_date you can do so (but you probably want to calculate the start of the month date_trunc can help with this).

Createing a report using financial periods

I have created a report for management that will total everything up by month with in a date range. Management has now decided that rather than by month they would like to go by period. We have 13 periods in a year each is 28 days except the last one is 29 or 30 depending on if its a leap year. The beginning of the first period is always 1-1-YYYY. So now I will need to figure out what the beginning and end of each period is and total up each period. I am not really sure how to do this since every year the dates will change and they may want to look at periods from the previous year through the current period. The code and results I am currently using are enclosed
SELECT
DATEADD(MONTH, DATEDIFF(MONTH, 0, finspecteddate), 0) AS 'Date'
,COUNT(*) AS Lots
,sum(flotSize) as 'Lot Size'
,sum(LReject) 'Lots Rejected'
,sum(fnumreject) as Rejected
,sum(fsampleSize) as 'Sample Size'
,sum(BDueDate) as 'Before Due Date'
FROM
ReportData
WHERE
finspecteddate >= '01-01-2014'
AND finspecteddate <= '10-15-2014'
GROUP BY
DATEADD(MONTH, DATEDIFF(MONTH, 0, finspecteddate), 0)
ORDER BY
date
Modify the following queries to suit your needs:
;WITH Period AS (
SELECT 1 AS ReportingPeriod,
CAST('2013-01-01' AS datetime) AS PeriodStartDate,
CAST('2013-01-28' AS datetime) AS PeriodEndDate
UNION ALL
SELECT CASE
WHEN p.ReportingPeriod = 13 THEN 1
ELSE p.ReportingPeriod + 1
END,
CASE
WHEN p.ReportingPeriod = 13 THEN DATEADD(YEAR,YEAR(p.PeriodStartDate)-1899,'1900-01-01')
ELSE DATEADD(DAY,28,p.PeriodStartDate)
END,
CASE
WHEN p.ReportingPeriod = 12 THEN DATEADD(YEAR,YEAR(p.PeriodStartDate)-1900,'1900-12-31')
ELSE DATEADD(DAY,28,p.PeriodEndDate)
END
FROM Period p
WHERE p.PeriodStartDate < '2017-12-03'
)
SELECT
P.PeriodStartDate
,P.PeriodEndDate
,COUNT(*) AS Lots
,sum(flotSize) as 'Lot Size'
,sum(LReject) 'Lots Rejected'
,sum(fnumreject) as Rejected
,sum(fsampleSize) as 'Sample Size'
,sum(BDueDate) as 'Before Due Date'
FROM
ReportData R
INNER JOIN Period P ON R.finspecteddate >= P.PeriodStartDate AND R.finspecteddate <= P.PeriodEndDate
WHERE
finspecteddate >= '01-01-2014'
AND finspecteddate <= '10-15-2014'
GROUP BY
P.PeriodStartDate
,P.PeriodEndDate
ORDER BY
P.PeriodStartDate
It uses a recursive CTE to build a period table, which is then joined to ReportData to aggregate asccording to your requirements. I don't have SQL Server 2005 to test it on. It works with 2008. Post a SQL Fiddle if you need help in 2005.
If you haven't got one, create a period calendar table with a year, period number, start date and end date columns. Then when you need to refer to periods, you can refer to the table. When they change the definition of what a period is, you can change the table. When they decide that February 29 doesn't count as one of the 28 days, you can change the table. When they decide to use the first Monday instead of the first Thursday as the start of the year, you just change the table. And best of all, changing how next year works won't change how last year works.
Then you just join to the table to determine which period you're in.

Difficult SELECT statement with only two columns

I have an SQL Server table structured as follows :
Table name : calendar.
Columns :
Calendar Date (smalldatetime)
Working Day (bit)
Calendar date has all dates, structured in the format yyyy-mm-dd. Working day means that I have work if it is a 1, and if it is a weekend or a holiday it is marked as a 0 :).
Example of what I want to retrieve :
Date NumWorkingDaysInMonthSoFar
------------------------------------
2013-06-01 0 --Due to this being marked a 0 as above (Saturday)
2013-06-02 0 --Due to this being marked a 0 as above (Sunday) -- All 3 of these dates marked as 0
2013-06-03 1 --Due to this being marked a 0 as above (Bank Holiday)
2013-06-04 1
2013-06-05 2
2013-06-06 3
Except I would like the query to work for ALL dates in the calendar - so if I ran it today, it would retrieve the number of working days in the month containing all of the calendar dates in my table above.
All of the information is there, but I am just not sure how to write a query like this or even where to begin, but I think I have gotten the barebones of it below :
SELECT Sum(Working Day)
WHERE DATEADD(dd, 0, DATEDIFF(mm, 0, CalendarDate)) between
GETDATE() and START_OF_MONTH
GROUP BY (Not a clue)
SQL Server 2012 has direct support for cumulative sums. For earlier versions, you need to do something like a fancy join or correlated subquery. I prefer the latter, because I think it is easier to read:
select c.[date],
(select sum(cast(WorkingDay as int))
from calendar c2
where year(c.[date]) = year(c2.[date]) and
month(c.[date]) = month(c2.[date]) and
c2.[date] <= c.[date]
) as NumWorkingDaysInMonth
from calendar c
In SQL Server 2012, you would just do:
select c.[date],
sum(cast(WorkingDay as int)) over (partition by year(c.[date], month[c.date])
order by c.[date]
) as NumWorkingDaysInMonth
from calendar c

How can I get a table of dates from the first day of the month, two months ago, to yesterday?

I have a situation that I would normally solve by creating a feeder table (for example, every date between five years ago and a hundred years into the future) for querying but, unfortunately, this particular job disallows creation of such a table.
So I'm opening this up to the SO community. Today is Jan 29, 2010. What query could I run that would give a table with a single date column with values ranging from Nov 1, 2009 through Jan 28, 2010 inclusive? On Feb 1, it should give me every date from Dec 1, 2009 through Jan 31, 2010.
I'm using DB2 but I'm happy to see any other solutions on the off-chance they may provide a clue.
I know I can select CURRENT DATE from sysibm.sysdummy1 (or dual for Oracle bods) but I'm not sure how to immediately select a date range without a physical backing table.
This just does sequential days between two dates, but I've posted to show you can eliminate the recursive error by supplying a limit.
with temp (level, seqdate) as
(select 1, date('2008-01-01')
from sysibm.sysdummy1
union all
select level, seqdate + level days
from temp
where level < 1000
and seqdate + 1 days < Date('2008-02-01')
)
select seqdate as CalendarDay
from temp
order by seqdate
Update from pax:
This answer actually put me on the right track. You can get rid of the warning by introducing a variable that's limited by a constant. The query above didn't have it quite right (and got the dates wrong, which I'll forgive) but, since it pointed me to the problem solution, it wins the prize.
The code below was the final working version (sans warning):
WITH DATERANGE(LEVEL,DT) AS (
SELECT 1, CURRENT DATE + (1 - DAY(CURRENT DATE)) DAYS - 2 MONTHS
FROM SYSIBM.SYSDUMMY1
UNION ALL SELECT LEVEL + 1, DT + 1 DAY
FROM DATERANGE
WHERE LEVEL < 1000 AND DT < CURRENT DATE - 1 DAY
) SELECT DT FROM DATERANGE;
which outputs, when run on the 2nd of February:
----------
DT
----------
2009-12-01
2009-12-02
2009-12-03
: : : :
2010-01-30
2010-01-31
2010-02-01
DSNE610I NUMBER OF ROWS DISPLAYED IS 63
DSNE616I STATEMENT EXECUTION WAS SUCCESSFUL.
just an idea (not even sure how you'd do this), but let's say you knew how many days you wanted. Like 45 days. If you could get a select to list 1-45 you could do date arithmetic to subtract that number from your reference data (ie today).
This kind of works (in MySQL):
set #i = 0;
SELECT #i:=#i+1 as myrow, ADDDATE(CURDATE(), -#i)
FROM some_table
LIMIT 10;
The trick i have is how to get the 10 to be dynamic. If you can do that then you can use a query like this to get the right number to limit.
SELECT DATEDIFF(DATE_SUB(CURDATE(), INTERVAL 1 DAY),
DATE_SUB(LAST_DAY(CURDATE()), INTERVAL 2 MONTH))
FROM dual;
I haven't used DB2 before but in SQL Server you could do something like the following. Note that you need a table with at least the number of rows as you need days.
SELECT TOP 45
DATEADD(d, ROW_NUMBER() OVER(ORDER BY [Field]) * -1, GETDATE())
FROM
[Table]