Combine with month and year column kept in different columns on Oracle - sql

I keep the month and year information in different columns as numbers.
I want to go back 12 months on sysdate using these columns.
The table I used below as an example ,
Since we are in the 5th month now, I want to get the data up to the 6th month of last year.
versiyon table :
So as a result of the query ,
the following result should return.
First of all, I want to query by combining the year and month columns and going back one year from the current month as a date.

Convert the values to strings and concatenate and then use ADD_MONTHS(SYSDATE, -12) to get last year's date (which will get the correct date regardless of whether it is a leap year or not):
SELECT *
FROM versiyon
WHERE TO_CHAR(year, 'fm0000') || TO_CHAR(month, 'fm00')
>= TO_CHAR(ADD_MONTHS(SYSDATE, -12), 'YYYYMM')
db<>fiddle here

select *
from versiyon
where lpad(year, 4, '0') || lpad(month, 2, '0') >= to_number(to_char(sysdate - 365, 'yyyymm' ))
Here is a demo
As MT0 say it will not work for leap years... so you can check if it is leap year or not with case when then end clause and using mod(year, 4) to check. In this demo I have created a situation as it is 2020, to be exact like it is 29th of February 2020. where you can see what I am suggesting in action:
DEMO2

You can convert your year and month into an integer form of YYYYMM and compare:
SELECT *
FROM versiyon_table
WHERE (versiyon_table.year * 100) + versiyon_table.month > (EXTRACT(YEAR FROM SYSDATE) * 100) + EXTRACT(MONTH FROM SYSDATE)

Related

Compare between two weeks of the year sqlite

I want to filter values from a table, between two weeks, like this:
select * from SalesWeekly
where SalesWeek BETWEEN '50' and '02'
Problem is, i have no idea how to specify week 50 is from year 2019,
and week 02 is from year 2020.
Assuming that you are storing the year in your table, say in table SalesYear, you could concatenate it with the week number and do string comparisons:
select *
from SalesWeekly
where SalesYear || '-' || SalesWeek BETWEEN '2019-50' and '2020-02'
For this to work, SalesWeek must be a 2-characters long string, left padded with 0 (so the 1st week should be '01', not '1').

Presto how to find start date given week

I want to find start date from given ISO week (which can range from 1-53, Monday as starting day) and year using Presto SQL query.
i.e. year - 2020 and week - 2 should return 06/01/2020
Is there any inbuilt function for this ?
Table structure:
select year, week from table1; // returns year and week from table table 1
There's no direct way for constructing a date from a year + week (there is an issue for this: https://github.com/trinodb/trino/issues/2287), but you can achieve what you want with the date_parse function.
For example:
WITH data (year, week) AS (VALUES (2020, 2))
SELECT CAST(date_parse(CAST(year AS varchar) || ':' || CAST(week AS varchar), '%x:%v') AS date)
FROM data
produces:
_col0
------------
2020-01-06
(1 row)
Using DATE_ADD and MAKEDATE you can achieve the result...
select DATE_ADD(MAKEDATE(year, 1), INTERVAL (week-1) WEEK) as start_date from <table_name>;
Martin's answer is almost there. Instead of using year, you should use year_of_week. Though with this change you'll have to make sure to not have a bug with weeks that bleed into the following or previous year i.e. last days of the previous year or first days of the next year.
year_of_week returns the year of the ISO week.
Here's an example:
WITH data (year, week) AS (VALUES (2020, 2))
SELECT CAST(date_parse(CAST(year_of_week AS varchar) || ':' || CAST(week AS varchar), '%x:%v') AS date)
FROM data
References:
https://prestodb.io/docs/current/functions/datetime.html#year_of_week
https://en.wikipedia.org/wiki/ISO_week_date
I think for partial weeks (when first days in a new year is still counted as week 53) this does not work:
Query failed (#20210624_142222_02859_zf75v): Cannot parse "2021:53": Value 53 for weekOfWeekyear must be in the range [1,52]
tested by this formula:
The date was on Jan 2,2021 which is still treated as week53 but in 2021...
CAST(date_parse(CAST(year(date(service_order_creation_date)) AS varchar) || ':' || CAST(week(date(service_order_creation_date)) AS varchar), '%x:%v') AS date)

SQL computing and reusing fiscal year calculation in sql query

I have a condition in my SQL query, using Oracle 11g database, that depends on a plan starting or ending with in a fiscal year:
(BUSPLAN.START_DATE BETWEEN (:YEAR || '-04-01') AND (:YEAR+1 || '-03-31')) OR
(BUSPLAN.END_DATE BETWEEN (:YEAR || '-04-01') AND (:YEAR+1 || '-03-31'))
For now, I am passing in YEAR as a parameter. It can be computed as (pseudocode):
IF CURRENT MONTH IN (JAN, FEB, MAR):
USE CURRENT YEAR // e.g. 2015
ELSE:
USE CURRENT YEAR + 1 // e.g. 2016
Is there a way I could computer the :YEAR parameter within in an SQL query and reuse it for the :YEAR parameter?
CTEs are easy, you can make little tables on the fly. With a 1 row table you just cross join it and then you have that value available every row:
WITH getyear as
(
SELECT
CASE WHEN to_char(sysdate,'mm') in ('01','02','03') THEN
EXTRACT(YEAR FROM sysdate)
ELSE
EXTRACT(YEAR FROM sysdate) + 1
END as ynum from dual
), mydates as
(
SELECT getyear.ynum || '-04-01' as startdate,
getyear.ynum+1 || '-03-31' as enddate
from getyear
)
select
-- your code here
from BUSPLAN, mydates -- this is a cross join
where
(BUSPLAN.START_DATE BETWEEN mydates.startdate AND mydates.enddate) OR
(BUSPLAN.END_DATE BETWEEN mydates.startdate AND mydates.enddate)
note, values statement is probably better if Oracle has values then the first CTE would look like this:
VALUES(CASE WHEN to_char(sysdate,'mm') in ('01','02','03') THEN
EXTRACT(YEAR FROM sysdate)
ELSE
EXTRACT(YEAR FROM sysdate) + 1)
I don't have access to Oracle so I might have bugs typos etc since I didn't test.
In the code you shared there is a problem and a potential problem.
Problem, implicit conversion to date without format string.
In (BUSPLAN.START_DATE BETWEEN (:YEAR || '-04-01') AND (:YEAR+1 || '-03-31')) two strings are being formed and then converted to dates. The conversion to date is going to change depending on the value of NLS_DATE_FORMAT. To insure that the string is converted correctly to_date(:YEAR || '-04-01', 'YYYY-MM-DD').
Potential problem, boundary at the end of the year when time <> midnight.
Oracle's date type holds both date and time. A test like someDate between startDate and endDate will miss all records that happened after midnight on endDate. One simple fix that precludes use of indexes on someDate is trunc(someDate) between startDate and endDate.
A more general approach is to define date ranges and closed open intervals. lowerBound <= aDate < upperBound where lowerBound is the same asstartDateabove andupperBoundisendDate` plus one day.
Note: Some applications used Oracle date columns as dates and always store midnight, if your application is of that sort, then this is not a problem. And check constraints like check (trunc(dateColumn) = dateColumn) would make sure it stays that way.
And now, to answer the question actually asked.
Using subquery factoring (Oracle's terminology) / common table expression (SQL Server's terminology) one can avoid repetition within a query.
Instead of figuring out the proper year, and then using strings to put together dates, the code below starts by getting January 1 at Midnight of the current calendar year, trunc(sysdate, 'YEAR')). Then it adds an offset in months. When the months are Jan, Feb, Mar, the current fiscal year started last year on 4/1, or nine months before the start of this year. The offset is -9. Else the current fiscal year started 4/1 of this calendar year, start of this year plus three months.
Instead of end date, an upper bound is calculated, similar to lower bound, but with the offsets being 12 greater than lower bound to get 4/1 the following year.
with current_fiscal_year as (select add_months(trunc(sysdate, 'YEAR')
, case when extract(month from sysdate) <= 3 then -9 else 3 end) as LowerBound
, add_months(trunc(sysdate, 'YEAR')
, case when extract(month from sysdate) <= 3 then 3 else 15 end) as UpperBound
from dual)
select *
from busplan
cross join current_fiscal_year CFY
where (CFY.LowerBound <= busplan.start_date and busplan.start_date < CFY.UpperBound)
or (CFY.LowerBound <= busplan.end_date and busplan.end_date < CFY.UpperBound)
And yet more unsolicited advise.
The times I've had to deal with fiscal year stuff, avoiding repetition within a query was low hanging fruit. Having the fiscal year calculations consistent and correct among many queries, that was the essence of the work. So I'd recommend a developing PL/SQL package that centralizes fiscal calculations. It might include a function like:
create or replace function GetFiscalYearStart(v_Date in date default sysdate)
return date
as begin
return add_months(trunc(v_Date, 'YEAR')
, case when extract(month from v_Date) <= 3 then -9 else 3 end);
end GetFiscalYearStart;
Then the query above becomes:
select *
from busplan
where (GetFiscalYearStart() <= busplan.start_date
and busplan.start_date < add_months(GetFiscalYearStart(), 12))
or (GetFiscalYearStart() <= busplan.end_date
and busplan.end_date < add_months(GetFiscalYearStart(), 12))

Timestamps and Intervals: NUMTOYMINTERVAL SYSTDATE CALCULATION SQL QUERY

I am working on a homework problem, I'm close but need some help with a data conversion I think. Or sysdate - start_date calculation
The question is:
Using the EX schema, write a SELECT statement that retrieves the date_id and start_date from the Date_Sample table (format below), followed by a column named Years_and_Months_Since_Start that uses an interval function to retrieve the number of years and months that have elapsed between the start_date and the sysdate. (Your values will vary based on the date you do this lab.) Display only the records with start dates having the month and day equal to Feb 28 (of any year).
DATE_ID START_DATE YEARS_AND_MONTHS_SINCE_START
2 Sunday , February 28, 1999 13-8
4 Monday , February 28, 2005 7-8
5 Tuesday , February 28, 2006 6-8
Our EX schema that refers to this question is simply a Date_Sample Table with two columns:
DATE_ID NUMBER NOT Null
START_DATE DATE
I Have written this code:
SELECT date_id, TO_CHAR(start_date, 'Day, MONTH DD, YYYY') AS start_date ,
NUMTOYMINTERVAL((SYSDATE - start_date), 'YEAR') AS years_and_months_since_start
FROM date_sample
WHERE TO_CHAR(start_date, 'MM/DD') = '02/28';
But my Years and months since start column is not working properly. It's getting very high numbers for years and months when the date calculated is from 1999-ish. ie, it should be 13-8 and I'm getting 5027-2 so I know it's not correct. I used NUMTOYMINTERVAL, which should be correct, but don't think the sysdate-start_date is working. Data Type for start_date is simply date. I tried ROUND but maybe need some help to get it right.
Something is wrong with my calculation and trying to figure out how to get the correct interval there. Not sure if I have provided enough information to everyone but I will let you know if I figure it out before you do.
It's a question from Murach's Oracle and SQL/PL book, chapter 17 if anyone else is trying to learn that chapter. Page 559.
you'll want MONTHS_BETWEEN in that numtoyminterval as the product of subtracting two date variables gives the answer in days which isn't usable to you and the reason its so high is you've told Oracle the answer was in years! Also use the fm modifier on the to_char to prevent excess whitespace.
select date_id,
to_char(start_date, 'fmDay, Month DD, YYYY') as start_date,
extract(year from numtoyminterval(months_between(trunc(sysdate), start_date), 'month') )
|| '-' ||
extract(month from numtoyminterval(months_between(trunc(sysdate), start_date), 'month') )
as years_and_months_since_start
from your_table
where to_char(start_date, 'MM/DD') = '02/28';
You can simplify the answer like this
SELECT date_id, start_date, numtoyminterval(months_between(sysdate, start_date), 'month') as "Years and Months Since Start"
FROM date_sample
WHERE EXTRACT (MONTH FROM start_date) = 2 AND EXTRACT (DAY FROM start_date) = 28;

How to convert month number to full month name in oracle?

I have a month column in my table. The month numbers are stored in this month column like 1 for january, 2 for feb and so on.
How do I convert the numbers into month names such as january, february, march etc.
SELECT TO_CHAR(TO_DATE(7, 'MM'), 'MONTH') AS monthname FROM DUAL;
outputs
monthname
---------
JULY
If you really want the month names in lower case or capitalised, you can also use:
TO_CHAR(TO_DATE(7, 'MM'), 'month')
TO_CHAR(TO_DATE(7, 'MM'), 'Month')
if you want to specify for your language, you can use like this;
SELECT to_char(TO_DATE(6, 'MM'),'MONTH','NLS_DATE_LANGUAGE=Turkish') from dual;
it returns a Turkish month name : Haziran
If you want to show a value after the formatted month like Year. Month tacks on a whole bunch of spaces which makes the value look weird. To fix this you have to trim the Month string prior to appending a follow up string value.
select trim(TO_CHAR(TO_DATE(7, 'MM'), 'MONTH')) || ' 2020' month_yr from dual;