select record for recent n years - sql

How to select the records for the 3 recent years, with JUST considering the year instead of the whole date dd-mm-yy.
let's say this year is 2013, the query should display all records from 2013,2012 and 2011.
if next year is 2014, the records shall be in 2014, 2013, 2012
and so on....
table foo
ID DATE
----- ---------
a0001 20-MAY-10
a0002 20-MAY-10
a0003 11-MAY-11
a0004 11-MAY-11
a0005 12-MAY-11
a0006 12-JUN-12
a0007 12-JUN-12
a0008 12-JUN-12
a0009 12-JUN-13
a0010 12-JUN-13

I think the clearest way is to use the year() function:
select *
from t
where year("date") >= 2011;
However, the use of a function in the where clause often prevents an index from being used. So, if you have a large amount of data and an index on the date column, then something that uses the current date is better. The following takes this approach:
select *
from t
where "date" >= trunc(add_months(sysdate, -3*12), 'yyyy')

SELECT ID, DATE FROM FOO
WHERE DATE >= TRUNC(ADD_MONTHS(SYSDATE, -3*12), 'YYYY');
Here we are getting last 36 months data by using ADD_MONTHS. We are adding 12*3 i.e. last 3 years data froom SYSDATE

That is pretty easy so I suspect that your question is not clear.
SELECT *
FROM foo
WHERE [date] >= '2011-01-01 00:00:00'
Does that work for you?
UPDATE
If you wish to do this dynamically you can use variables.
Below is how you would do that in SQL Server (I do not know the Oracle syntax for this)
DECLARE #startYear int
SET #startYear = YEAR(GETDATE()) - 2
SELECT *
FROM foo
WHERE [date] >= #startYear + '-01-01 00:00:00'

Related

Snowflake SQL: Is there a way to select records between 2 years back and the current date?

I have seen a lot of questions similar to this but I have yet to see one that goes into detail of how to get records from two years back to today but include the start of the year two years back. Meaning I would like to create a function that will always give me results from January 1st two years back. For this year the results would come from 01-01-2020 to today’s date.
This is what I have so far, but in reality I am using it for a temporary table in my query.
SELECT *
FROM final
WHERE order_date BETWEEN DATEADD(‘year’, -2, current_date) AND current_date
You may use a combination of DATE_TRUNC and DATEADD to find January 1 of two years ago.
SELECT *
FROM final
WHERE order_date BETWEEN DATE_TRUNC('year', DATEADD('year', -2, current_date)) AND current_date;
What you had is close. Just truncate the the year.
You can see what happens in isolation:
select trunc('2021-03-14 08:24:12'::timestamp, 'YEAR');
-- Output: 2021-01-01 00:00:00.000
Adding to your SQL:
SELECT *
FROM final
WHERE order_date
BETWEEN trunc(DATEADD(‘year’, -2, current_date), 'YEAR') AND current_date
It is possible to construct arbitrary date using DATE_FROM_PARTS:
Creates a date from individual numeric components that represent the year, month, and day of the month.
DATE_FROM_PARTS( <year>, <month>, <day> )
For current_date:
SELECT DATE_FROM_PARTS(YEAR(current_date)-2,1,1);
-- 2020-01-01
Full query:
SELECT *
FROM final
WHERE order_date BETWEEN DATE_FROM_PARTS(YEAR(current_date)-2,1,1) AND current_date
This should be enough
where year(current_date)-year(order_date)<=2
But in case you have an order_date from the future
where year(current_date)-year(order_date)<=2 and order_date<=current_date

How to generate dynamic table having begin month date and end month date Teradata or SAS SQL

I’d like to generate a dynamic Table with the start date of a month as a column and the end date of the month as another column.
Ideally, I’d like to provide two years, f.e. 2016 and 2021. The output I’d like to have when providing these two years is the following:
Begin_of_Month End_of_Month
2016-01-01 2016-01-31
2016-02-01 2016-02-29
.
.
.
2021-12-01 2021-12-31
Kindly note that I require the output for all years from 2016 to 2021. In my example above this would mean that 2017 2018 2019 2020 should be included.
I have tried to play with a time series function of Teradata but failed to get results.
The solution I tried to recreate in Teradata is this one: How to generate calendar table having begin month date and end month Date
Furthermore, I have tried the EXPAND ON PERIOD time series function of Teradata.
I'm sure there are some fancy ways of doing this, but I think just hitting up the built-in calendar table is probably the easiest:
SELECT DISTINCT
min(calendar_date) OVER (PARTITION BY year_of_calendar, month_of_calendar) as start_of_month,
max(calendar_date) OVER (PARTITION BY year_of_calendar, month_of_calendar) as end_of_month
FROM sys_calendar.calendar
WHERE year_of_calendar BETWEEN 2016 and 2021
To do this without a table reference, it gets a little ugly. EXPAND ON seems like an obvious route, but it errors if there is no table reference in the FROM clause. UNION suffers from the same issue, but we can cheat with UNION by using a cte. EXPAND ON is more picky and to trick it we can hijack Teradata's JSON_TABLE feature:
SELECT BEGIN(dt), PRIOR(END(dt))
FROM JSON_TABLE
(
ON (SELECT 1 as id, NEW JSON('{"startdate":"2016-01-01","enddate":"2021-12-31"}') jd)
USING
rowexpr('$')
colexpr('[{"jsonpath" : "$.startdate", "type" : "DATE"},
{"jsonpath" : "$.enddate", "type" : "DATE"}]')
) as jt(id, startdate, enddate)
EXPAND ON PERIOD(startdate, enddate) as dt BY ANCHOR MONTH_BEGIN
You could also go with a recursive CTE to build out the months, which feels less hacky, but takes longer to generate.
WITH startend AS
(
SELECT
DATE '2016-01-01' periodstartdate,
DATE '2021-12-31' AS periodenddate
)
,RECURSIVE months AS
(
SELECT periodstartdate,
periodenddate,
periodstartdate as monthstartdate,
1 as monthoffset
FROM startend
UNION ALL
SELECT periodstartdate,
periodenddate,
ADD_MONTHS(periodstartdate, monthoffset),
monthoffset + 1
FROM
months
WHERE monthoffset < months_between(periodenddate, periodstartdate)
)
SELECT monthstartdate, monthstartdate + INTERVAL '1' MONTH - INTERVAL '1' DAY as monthenddate from months;
I'd be very interested if there is a more elegant way to pull this off. Without dual or sequence generation like are present in other RDBMS, the options to build data sets with no table reference are pretty limited.
Normally EXPAND ON only works when a table is accessed in FROM, but applying some function like TRUNC or TO_DATE fools the optimizer:
WITH dummy AS
(
SELECT
2016 AS yr_start
,2021 as yr_end
,TO_DATE(TRIM(yr_start) || '-01-01') AS pd_start
,TO_DATE(TRIM(yr_end+1) || '-01-01') AS pd_end
)
SELECT
BEGIN(pd) AS Begin_of_Month
,LAST(pd) AS End_of_Month
FROM dummy
EXPAND ON PERIOD(pd_start, pd_end) AS pd
BY INTERVAL '1' MONTH
If you are going to do this in SAS then there is no need for SQL.
data want;
do year=2016 to 2021;
do month=1 to 12;
start_of_month=mdy(month,1,year);
end_of_month=intnx('month',start_of_month,0,'e');
output;
end;
end;
format start_of_month end_of_month yymmdd10.;
drop year month;
run;

How to get the exact year difference in SQL

I need a simple way to find the exact year difference between two dates.
For example between 01.11.2013 and 30.10.2019. In this case, it should be 5 years because current date has not reached 01.11 yet. But if it is between 29.10.2013 and 30.10.2019 then it should be 6 years.
Let assume these are birthday and current days. It's similar.
I've tried to get the result with the following code:
select datediff(year,20131101,20191030)
It is giving me 6 instead of 5. The closest solution I've found is:
select datediff(day,20131101,20191030)/365
But as we know there could be 1 or 2 leap years in this period and the result is again not exactly what I expect. Neither when we divide with 365 nor 366.
It is not necessary using datediff. I wonder if there is some simple way to present it or some stored function to do this for which existing I did't know.
You can take the year difference and then subtract 1 if necessary:
select (datediff(year, val1, val2) -
(case when month(val1) < month(val2) then 0
when month(val1) > month(val2) then 1
when day(val1) <= day(val2) then 0
else 1
end)
) as diff_years
You can try datediff Month / 12.
Declare #date1 Datetime
Declare #date2 Datetime
set #date1='2013-11-01 23:59:59'
set #date2='2019-10-30 00:00:00'
(select (datediff(HOUR, #date1, #date2)/24/365 ) as diff_years)

Recurring events reminder in 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

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]