Filter last month even in January - sql

The filter below works for most months. However, in January I also want to pull December of the last year.
EXTRACT(MONTH FROM SYSDATE)-1 returns 0
and
EXTRACT(YEAR FROM SYSDATE)
How can I change this filter to make it more dynamic for every month of the year.
WHERE MONTH= EXTRACT(MONTH FROM SYSDATE)-1 AND YEAR = EXTRACT(YEAR FROM SYSDATE)

Even simpler:
WHERE month = TO_NUMBER(TO_CHAR(ADD_MONTHS(SYSDATE,-1),'MM'))
AND year = TO_NUMBER(TO_CHAR(ADD_MONTHS(SYSDATE,-1),'YYYY'))

Subtract a month before using EXTRACT:
WHERE MONTH = EXTRACT(MONTH FROM ADD_MONTHS(SYSDATE, -1))
AND YEAR = EXTRACT(YEAR FROM ADD_MONTHS(SYSDATE, -1))

Something like this, perhaps? ADD_MONTHS will subtract 1 month from sysdate (the result is DATE datatype so you have to apply to_char to it and, possibly, to_number (depending on what the month column contains)). Year, on the other hand, depends on current month - if it is January, take previous year; otherwise, take current year.
where month = to_char(add_months(sysdate, -1), 'mm')
and year = case when extract(month from sysdate) = 1 then extract(year from sysdate) - 1
else extract(year from sysdate)
end

Related

Returning a Date Range based on Day of the Month

Select *
From Orders
WHERE (
extract(day from sysdate)<=21
and
to_date(SCHEDULEDATE , 'yyyy/mm/dd') between
to_date((to_char(sysdate, 'YYYY')||'/'||cast((extract(month from sysdate)-1)as char)||'/22'),'yyyy/mm/dd') and to_date((to_char(sysdate,'YYYY')||'/'||cast((extract(month from sysdate))as char)||'/21'),'yyyy/mm/dd')
)
or
(
extract(day from sysdate)>21
and
to_date(SCHEDULEDATE , 'yyyy/mm/dd') between
to_date((to_char(sysdate, 'YYYY')||'/'||cast((extract(month from sysdate))as char)||'/22'),'yyyy/mm/dd') and to_date((to_char(sysdate, 'YYYY')||'/'||cast((extract(month from sysdate)+1)as char)||'/21'),'yyyy/mm/dd')
)
I'm trying to figure out a simple way of returning a set of date ranges based on the day of the Month. If the Day of the month of less than or Equal to I want it to return all orders the have a schedule date between the 22 of the Month before and the 21st of the Current month. If the Day of the month is greater than 21 I would like it return all orders that have a schedule date of the current month up to the end of the month. I've tried to use a case in the where with no luck. What I have now doesn't seem to work either. Any help would be appreciated.
I think this does what you want:
WHERE (extract(day from sysdate) <= 21 and
scheduledate >= add_months(trunc(sysdate, 'MON'), -1) + 21 and
scheduledate < trunc(sysdate, 'MON') + 21
) or
(extract(day from sysdate) > 21 and
trunc(scheduledate, 'MON') = trunc(sysdate, 'MON')
)

Extract Month in sql and adding 1

If I add 1 to extract(month from date), then does the result become 13 or 1 (January of next year)
I have the below code:
(extract(day from sysdate) >=1 and extract(month from sysdate)=12) and (targstartdate >= to_date(((extract(month from sysdate))|| '-1-' || (extract(year from sysdate)+1)) , 'mm-dd-yyyy') and targstartdate <= to_date(((extract(month from sysdate)+1)|| '-1-' ||(extract(year from sysdate)+1)) , 'mm-dd-yyyy')
You can use MOD
MOD(extract(month from sysdate)+1,12) + 1
If you are trying to get the month number of the next month, flip your logic. Instead of extracting the month number and adding 1, add one month to the date then extract the month. See the difference below. The second adds one month, then extracts the month of 1 (for January).
select extract( month from to_date('12/15/2017','MM/DD/YYYY')) + 1 from dual
union all
select extract (month from ADD_MONTHS(to_date('12/15/2017','MM/DD/YYYY'),1)) from dual;

Select data with a rolling date criteria

The below query returns a distinct count of 'members' for a given month and brand (see image below).
select to_char(transaction_date, 'YYYY-MM') as month, brand,
count(distinct UNIQUE_MEM_ID) as distinct_count
from source.table
group by to_char(transaction_date, 'YYYY-MM'), brand;
The data is collected with a 15 day lag after the month closes (meaning September 2016 MONTHLY data won't be 100% until October 15). I am only concerned with monthly data.
The query I would like to build: Until the 15th of this month (October), last month's data (September) should reflect August's data. The current partial month (October) should default to the prior month and thus also to the above logic.
After the 15th of this month, last month's data (September) is now 100% and thus September should reflect September (and October will reflect September until November 15th, and so on).
The current partial month will always = the prior month. The complexity of the query is how to calc prior month.
This query will be ran on a rolling basis so needs to be dynamic.
To be clear, I am trying to build a query where distinct_count for the prior month (until end of current month + 15 days) should reflect (current month - 2) value (for each respective brand). After 15 days of the close of the month, prior month = (current month - 1).
Partial current month defaults to prior month's data. The 15 day value should be variable/modifiable.
First, simplify the query to:
select to_char(transaction_date, 'YYYY-MM') as month, brand,
count(distinct members) as distinct_count
from source.table
group by members, to_char(transaction_date, 'YYYY-MM'), brand;
Then, you are going to have a problem. The problem is that one row (say from Aug 20th) needs to go into two groups. A simple group by won't handle this. So, let's use union all. I think the result is something like this:
select date_trunc('month', transaction_date) as month, brand,
count(distinct members) as distinct_count
from source.table
where (date_trunc('month', transaction_date) < date_trunc('month' current_date) - interval '1 month') or
(day(current_date) > 15 and date_trunc('month', transaction_date) = date_trunc('month' current_date) - interval '1 month')
group by date_trunc('month', transaction_date), brand
union all
select date_trunc('month' current_date) - interval '1 month' as month, brand,
count(distinct members) as distinct_count
from source.table
where (day(current_date) < 15 and date_trunc('month', transaction_date) = date_trunc('month' current_date) - interval '1 month')
group by brand;
Since you already have a working query, I concentrate on the subselect. The condition you can use here is CASE, especially "Searched CASE"
case
when extract(day from current_date) < 15 then
extract(month from current_date - interval '2 months')
else
extract(month from current_date - interval '1 month')
end case
This may be used as part of a where clause, for example.
Here is some sudo code to get the begin date and the end date for your interval.
Begin date:
date DATE_TRUNC('month', CURRENT_DATE - integer 15) - interval '1 month'
This will return the current month only after the 15th day, from there you can subtract a full month to get your starting point.
End Date:
To calculate this, grab the begin date, plus a month, minus a day.
If the source table is partitioned by transaction_date, this syntax (not masking transaction_date with expression) enables partitions eliminatation.
select to_char(transaction_date, 'YYYY-MM') as month
,count (distinct members) as distinct_count
,brand as brand
FROM source.table
where transaction_date between date_trunc('month', current_date) - case when extract (day from current_date) >= 15 then 1 else 2 end * interval '1' month
and date_trunc('month', current_date) - case when extract (day from current_date) >= 15 then 0 else 1 end * interval '1' month - interval '1' day
group by to_char(transaction_date, 'YYYY-MM')
,brand
;

Oracle SQL to get first day of month

Requirement is input date should produce first day of the month.
Condtions are:
If the date entered is between 16-nov to 30-nov, then the first day will be 16-nov.
if the date entered is between 1-nov to 15-nov , then the first day will be 01-nov.
for all other month it should return 01st day of corresponding month.
Building on Tim Biegeleisen's solution, simplifying it and avoid the date-to-text-to-date conversions. Note the use of TRUNC to get the first date of the period.
SELECT
CASE
WHEN EXTRACT(MONTH FROM DATE_COL) = 11 AND EXTRACT(DAY FROM DATE_COL) >= 16
THEN TRUNC(DATE_COL, 'MONTH') + 15
ELSE TRUNC(DATE_COL, 'MONTH')
END AS FIRST_OF_MONTH
FROM T1
I used a lengthy CASE expression to handle this, containing the logic for the three cases you mentioned in your question.
SELECT CASE WHEN EXTRACT(month FROM date) = 11 AND
EXTRACT(day FROM date) >= 16
THEN TO_DATE(EXTRACT(year FROM date) || '-11-16', 'yyyy-mm-dd')
ELSE TO_DATE(EXTRACT(year FROM date) || '-' || EXTRACT(month FROM date) ||
'-01', 'yyyy-mm-dd')
END AS newDate
FROM yourTable

How to get start and end dates of current month

How to get start date and end dates in a query from database.
Thanks,
Lico
http://sqltutorials.blogspot.com/2007/06/sql-first-and-last-day-of-month.html
Only thing that isn't covered is how to retrieve the current date... see rexem's post.
CURRENT_DATE[()] | CURDATE() | SYSDATE | TODAY = returns current date
Start of month:
DATE EXTRACT(YEAR FROM SYSDATE) +'-'+ EXTRACT(MONTH FROM SYSDATE) +'-01'
For completeness, start of month:
DATE EXTRACT(YEAR FROM SYSDATE) +'-'+ EXTRACT(MONTH FROM SYSDATE) +'-01'
End of month:
DATEADD('DAY', -1, DATEADD('MONTH', 1, DATE EXTRACT(YEAR FROM SYSDATE) +'-'+ EXTRACT(MONTH FROM SYSDATE) +'-01'))