ORACLE SQL QUERY SYSDATE - sql

I need help with my query
select distinct count(item_number), creation_date
from EGP_SYSTEM_ITEMS_B ,
all I need to count the item number every month
for example
3-9-2020 count:29700
4-9-2020 count:29600
5-9-2020 Count:30000
and get the all date for the month and the previous month from creation_id or sysdate any of them
thanks

To count the number per month, you would aggregate by the month:
select trunc(creation_date, 'MON') as yyyymm, count(*)
from EGP_SYSTEM_ITEMS_B
group by trunc(creation_date, 'MON');

Your question is not entirely clear; perhaps you're trying to get
select TRUNC(CREATION_DATE), count(item_number)
from EGP_SYSTEM_ITEMS_B
WHERE TRUNC(CREATION_DATE)
IN (ADD_MONTHS(TRUNC(SYSDATE), -1),
TRUNC(SYSDATE),
ADD_MONTHS(TRUNC(SYSDATE), 1))
GROUP BY TRUNC(CREATION_DATE)
EDIT
Apparently OP wants a running monthly count, so something like:
WITH cteLimits (START_DATE, END_DATE)
AS (SELECT ADD_MONTHS(TRUNC(SYSDATE), -2), ADD_MONTHS(TRUNC(SYSDATE), -1) - INTERVAL '1' DAY FROM DUAL UNION ALL
SELECT ADD_MONTHS(TRUNC(SYSDATE), -1), TRUNC(SYSDATE) - INTERVAL '1' DAY FROM DUAL UNION ALL
SELECT TRUNC(SYSDATE), ADD_MONTHS(TRUNC(SYSDATE), 1) - INTERVAL '1' DAY FROM DUAL),
cteDay_totals
AS (SELECT TRUNC(CREATION_DATE) AS CREATION_DATE,
COUNT(*) AS DAY_TOTAL
FROM EGP_SYSTEM_ITEMS_B
GROUP BY TRUNC(CREATION_DATE))
SELECT l.START_DATE,
l.END_DATE,
SUM(d.DAY_TOTAL) AS MONTH_TOTAL
FROM cteLimits l
INNER JOIN cteDay_totals d
ON d.CREATION_DATE BETWEEN l.START_DATE AND l.END_DATE
GROUP BY l.START_DATE,
l.END_DATE

Related

Getting a period index from a date in PostgreSQL

Here is a Postgres code I created, it works. Is there a way to code it in a more efficient way? My goal is to get how much periods a given date falls from 2014-03-01. One period is a half-year starting from March or September.
I updated this code below on 2022-05-18 at 10:19 UTC+2
select date,
dense_rank() over (order by half_year_mar_sep) as period_index
from
(
select date as date,
case when extract(month from date) = 12 then (extract(year from date) || '-09-01')
when extract(month from date) in (1, 2) then (extract(year from date) - 1 || '-09-01')
when extract(month from date) in (3, 4, 5) then (extract(year from date) || '-03-01')
when extract(month from date) in (6, 7, 8) then (extract(year from date) || '-03-01')
else extract(year from date) || '-09-01'
end::date as half_year_mar_sep
from
(
select generate_series(date '2014-03-01', CURRENT_DATE, interval '1 day')::date as date
) s1
) s2
If I encapsulate the code above into select min(date), period_index from (<code above>) s3 group by 2 order by 1 then here is the result what I need:
WITH cte AS (
SELECT
date1::date,
rank() OVER (ORDER BY date1)
FROM generate_series(date '2014-03-01', CURRENT_DATE + interval '1' month, interval '6 month') g (date1)
),
cteall AS (
SELECT
all_date::date
FROM
generate_series(date '2014-03-01', CURRENT_DATE + interval '1' month, interval ' 1 day') s (all_date)
),
cte3 AS (
SELECT
*
FROM
cteall c1
LEFT JOIN cte c2 ON date1 = all_date
),
cte4 AS (
SELECT
*,
count(rank) OVER w AS ct_str
FROM
cte3
WINDOW w AS (ORDER BY all_date))
SELECT
*,
rank() OVER (PARTITION BY ct_str ORDER BY all_date) AS rank1,
dense_rank() OVER (ORDER BY all_date) AS dense_rank1
FROM
cte4;
Hope it's not intimidating. personally I found cte is a good tool, since it make logic more clearly.
demo
useful link: How to do forward fill as a PL/PGSQL function
If some column don't need, you can simple replace * with the columns you want.
Based on #Mark's answer I wrote this code below, but it's not simpler than the original code.
select s.date,
m.period_index
from
(
select date::date as half_year_start,
rank() over (order by date) as period_index,
coalesce(lead(date::date, 1) over (), CURRENT_DATE) as following_half_year_start
from generate_series(date '2014-03-01', CURRENT_DATE + interval '1' month, interval '6 month') as date
) m
left join
(
select generate_series(date '2014-03-01', CURRENT_DATE, interval '1 day')::date as date
) s
on s.date between m.half_year_start and m.following_half_year_start
;

How to put Case in Where Statement for Oracle SQL

For the query below, I'm trying to pull a specific date range depending on the current day of the month. If it's the 20th or less (e.g. "2/7/2020") then I want the date range for January. Otherwise, I want the date range for February. Is it possible to be done with a case statement? Or there is a better way?
SELECT
account,
start_date,
amount
FROM
table1
WHERE
CASE
WHEN (
SELECT
CAST(EXTRACT(DAY FROM sysdate) AS NUMBER)
FROM
dual
) <= 20 THEN
start_date
BETWEEN '2020-01-01' AND '2020-01-31'
ELSE start_date BETWEEN '2020-02-01' AND '2020-02-29'
END
You can do this by avoiding the case statement and using truncate the date - 20 to the month, e.g.:
SELECT account,
start_date,
amount
FROM table1
WHERE start_date >= TRUNC(SYSDATE - 20, 'mm')
AND start_date < add_months(TRUNC(dt - 20, 'mm'), 1);
If you really had to use a CASE expression (you can't use a CASE statement in SQL), you would need to do something like:
SELECT account,
start_date,
amount
FROM table1
WHERE start_date >= CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN add_months(TRUNC(SYSDATE, 'mm'), -1) ELSE TRUNC(SYSDATE, 'mm') END
AND start_date < CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN TRUNC(SYSDATE, 'mm') ELSE add_months(TRUNC(SYSDATE, 'mm'), 1) END;
N.B. if you're using a function, you don't need to wrap it in a select .. from dual, you can use it directly in the SQL statement.
I've also assumed that you want a dynamic range, e.g. if the day of the month is 20 or less, the range is for the previous month, otherwise the current month.
ETA: You would use the above two queries if there is an index on the start_date column, otherwise you could simply do:
SELECT account,
start_date,
amount
FROM table1
WHERE TRUNC(start_date, 'mm') = TRUNC(SYSDATE - 20, 'mm');
Case statements return single values. As such you should pull out the start date and you'll need two case statements.
select account, start_date, amount
from table1 where
start_date between
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-01'
else '2020-02-01'
end) and
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-31'
else '2020-02-29'
end)
One method subtracts 20 days and then gets the month boundary:
where start_date >= trunc(sysdate - interval '20' day, 'MON') and
start_date < trunc(sysdate - interval '20' day, 'MON') + interval '1' month
This approach is index (and partition) friendly -- an appropriate index on start_date can be used. It is also safe if start_date has time components.
Note: You can use sysdate without having to use a subquery.
You can use or operator with last_day function as following:
Select * from your_table
Where (
start_date <= trunc(sysdate,'mm') + 20
and start_date between trunc(sysdate,'mm') - interval '1' month and trunc(sysdate,'mm') - 1
)
Or
(
start_date > trunc(sysdate,'mm') + 20
and start_date between trunc(sysdate, 'mm') and last_day(sysdate)
)
This approach will use index on start_date, if any.
Cheers!!
select account, amount, start_date
from table1
where ( ( (select cast (extract (day from sysdate) as number) from dual) <= 20
and start_date between date '2020-01-01' and date '2020-01-31')
or ( (select cast (extract (day from sysdate) as number) from dual) > 20
and start_date between date '2020-02-01' and date '2020-02-29')
);
Using CASE expressions as BETWEEN operands:
SELECT account
, start_date
, amount
FROM table1
WHERE start_date BETWEEN CASE
WHEN extract(day from sysdate) <= 20
THEN trunc(sysdate -interval '1' month, 'month')
ELSE trunc(sysdate, 'month')
END
AND CASE
WHEN extract(day from sysdate) <= 20
THEN last_day(sysdate -interval '1' month)
ELSE last_day(sysdate)
END

Get the last day of each month in the last two years using oracle SQL

I am new to Oracle SQL. I know the LAST_DAY function will return the last day of the month given a specific date. But how can I get the last day of each month in the last two years? Thanks so much!
SELECT TRUNC (ADD_MONTHS (SYSDATE, -(LEVEL - 1)), 'MM') FIRST_DAY,
LAST_DAY (ADD_MONTHS (SYSDATE, -(LEVEL - 1))) LAST_DAY
FROM DUAL
CONNECT BY LEVEL <= 24;
Here you go:
WITH cteTwo_years_ago AS (SELECT TRUNC(SYSDATE - INTERVAL '2' YEAR, 'MONTH') AS TWO_YEARS_AGO
FROM DUAL),
cteMonth_intervals AS (SELECT INTERVAL '1' MONTH * (LEVEL-1) AS SEQ
FROM DUAL
CONNECT BY LEVEL-1 < 24),
cteDates AS (SELECT TWO_YEARS_AGO + SEQ AS MONTH_START
FROM cteTwo_years_ago
CROSS JOIN cteMonth_intervals)
SELECT LAST_DAY(MONTH_START)
FROM cteDates
First and last date of each month in interval from given date up today.
DEFINE start_date=TO_DATE('01.01.2020','DD.MM.YYYY');
SELECT
ADD_MONTHS(&start_date,LEVEL-1) first_day_in_month,
ADD_MONTHS(&start_date,LEVEL)-1 last_day_in_month
FROM DUAL CONNECT BY LEVEL <= FLOOR(MONTHS_BETWEEN(SYSDATE,&start_date));

get second and fourth saturday along with all sundays of the previous month in oracle

I have the following query which gives the second and fourth Saturdays of the previous month along with all the Sundays of the previous month:
SELECT to_char(NEXT_DAY(NEXT_DAY(NEXT_DAY(NEXT_DAY(TRUNC((SELECT LAST_DAY(ADD_MONTHS(sysdate,-1)) FROM DUAL), 'MONTH') - 1, 'SATURDAY'), 'SATURDAY'),'SATURDAY'),'SATURDAY'),'YYYYMMDD') SECOND_SATURDAY
FROM DUAL
UNION ALL
SELECT to_char(NEXT_DAY(NEXT_DAY(TRUNC((SELECT LAST_DAY(ADD_MONTHS(sysdate,-1)) FROM DUAL), 'MONTH') - 1, 'SATURDAY'),'SATURDAY'),'YYYYMMDD') SECOND_SATURDAY
FROM DUAL
UNION ALL
select distinct day_date from
(SELECT to_char(NEXT_DAY(LEVEL + TRUNC((SELECT LAST_DAY(ADD_MONTHS(sysdate,-1)) FROM DUAL), 'MONTH') - 1,'SUNDAY'),'YYYYMMDD') day_date
FROM DUAL
CONNECT BY LEVEL <= ADD_MONTHS(TRUNC((SELECT LAST_DAY(ADD_MONTHS(sysdate,-1)) FROM DUAL), 'MONTH'), 1) - TRUNC((SELECT LAST_DAY(ADD_MONTHS(sysdate,-1)) FROM DUAL), 'MONTH'))
where substr(day_date,1,6) in (select to_char((SELECT LAST_DAY(ADD_MONTHS(sysdate,-1)) FROM DUAL),'YYYYMM') from dual)
But I feel there must be a simpler way to get the same result in oracle.Any help in this regard is welcome. My requirement for the date format is 'YYYYMMDD'.
Edited because I misunderstood the question:
I think you want something like the following:
SELECT TO_CHAR(my_date, 'YYYYMMDD') AS my_formatted_date
FROM (
SELECT NEXT_DAY(LAST_DAY(ADD_MONTHS(SYSDATE, -2))+7, 'SATURDAY') AS my_date
FROM dual
UNION ALL
SELECT NEXT_DAY(LAST_DAY(ADD_MONTHS(SYSDATE, -2))+21, 'SATURDAY')
FROM dual
UNION ALL
SELECT NEXT_DAY(LAST_DAY(ADD_MONTHS(SYSDATE, -2)), 'SUNDAY') + (LEVEL-1)*7
FROM dual
CONNECT BY NEXT_DAY(LAST_DAY(ADD_MONTHS(SYSDATE, -2)), 'SUNDAY') + (LEVEL-1)*7 < TRUNC(SYSDATE, 'MONTH')
);
In the first part of the query I determine the last day of the previous month and then get the first Saturday that falls after at least one week after that date (the second Saturday of the month will be anywhere from the 8th to the 14th). Then I get the first Saturday falling at least three weeks after the last day of the previous month (the fourth Saturday will be anywhere from the 22nd to the 28th). Last, I loop over the Sundays of the previous month, starting from the first Sunday falling after the last day of the month previous to that (that is, two months ago). You could also use NEXT_DAY(TRUNC(ADD_MONTHS(SYSDATE, -1) - 1), 'SUNDAY') instead of NEXT_DAY(LAST_DAY(ADD_MONTHS(SYSDATE, -2)), 'SUNDAY')
MY_FORMA
--------
20190209
20190223
20190203
20190210
20190217
20190224
6 rows selected.
For instance like here:
with
t1 as (select add_months(trunc(sysdate, 'month'), -1) dt from dual),
t2 as (
select dt + level - 1 dt, to_char(dt + level - 1, 'dy', 'nls_date_language=english') dy
from t1 connect by dt + level - 1 < trunc(sysdate, 'month'))
select to_char(dt, 'yyyymmdd') dt, dy
from (
select dt, dy, sum(case when dy = 'sat' then 1 end) over (order by dt) sm from t2)
where dy = 'sun' or (dy = 'sat' and sm in (2, 4))
Result:
DT DY
-------- ---
20190203 sun
20190209 sat
20190210 sun
20190217 sun
20190223 sat
20190224 sun
I generated all days for previous month, assigned english day names, conditionally counted saturdays and showed only interesting dates.
Edit:
This works but is there any way to get around the with clause since my
parent query is of the from: with x as ( .... where date in ("required
days") and ...) . So it presents a nested with situation.
Yes, you can convert it easily, for example like here:
select to_char(dt, 'yyyymmdd') dt
from (
select dt, dn, rank() over (order by mod(dn, 6), dt) rnk
from (
select d + level - 1 dt, d + level - trunc(d + level - 1, 'iw') dn
from (select add_months(trunc(sysdate, 'month'), -1) d from dual)
connect by level <= add_months(d, 1) - d))
where dn = 7 or (dn = 6 and rnk in (2, 4))
dbfiddle demo
with clause made steps more readable. Now I also used day numbers and mod() for counting, just to show different approaches, but you can use whatever is clearer for you (day names, sum or count instead of rank).

Netezza range of dates

I want to solve an issue to reduce manual labour in a specific query. Hope i can phrase this correctly to be understood.
In Netezza, I want to generate a (date ) value and run the query for every different value specified.
What I want to do is replace all the unions into one query.
SELECT DISTINCT COUNT(DISTINCT a.x) AS NO_OF_X, a.column1, 'JAN 2019'
FROM my_table a
WHERE 1=1
AND current_date BETWEEN a.date_from and a.date_to
GROUP BY 2,3
UNION ALL
SELECT DISTINCT COUNT(DISTINCT a.x) AS NO_OF_X, a.column1, 'DEC 2018'
FROM my_table a
WHERE 1=1
AND '2018-12-31' BETWEEN a.date_from and a.date_to
GROUP BY 2,3
UNION ALL
SELECT DISTINCT COUNT(DISTINCT a.x) AS NO_OF_X, a.column1, 'NOV 2018'
FROM my_table a
WHERE 1=1
AND '2018-11-30' BETWEEN a.date_from and a.date_to
GROUP BY 2,3
What i want to do is something like this
SELECT DISTINCT COUNT(DISTINCT a.x) AS NO_OF_X, a.column1, last_day( date ) as "MONTH"
FROM my_table a
WHERE 1=1
AND /*run the query for all last_days in a range */
GROUP BY 2,3
Is this possible? Tried to make a CTE but it is really important to get results for each specific last day of month, because our datawarehouse is designed, to store different time slices for each transaction etc. And i want to get only transactions with time slices on a specific last_day().
Cheers.
You need to generate a list of dates. Here is one method:
select to_char(m.dte, 'MMM YYYY'), t.column1,
count(distinct a.x) AS NO_OF_X
from (SELECT current_date as dte UNION ALL
SELECT date_trunc('month', current_date) - interval '1 day' as dte UNION ALL
SELECT date_trunc('month', current_date) - interval '1 day' - interval '1 month' as dte UNION ALL
SELECT date_trunc('month', current_date) - interval '1 day' - interval '2 month' as dte
) m left join
my_table t
where m.dte between t.date_from and t.date_to
group by to_char(m.dte, 'MMM YYYY'), t.column1
order by min(m.dte), t.column1;