i have column it`s look like that https://i.stack.imgur.com/j7Nwh.png it is format +Year-Month an i need to extract some information for example:
If the period is 0 return "No Warranty"
if the period is more than 0 return number year number months ...
any suggestion how to approach, I know I have to use some Case or IF Else structure
Use a CASE expression:
SELECT warranty_period,
CASE warranty_period
WHEN INTERVAL '0-0' YEAR TO MONTH
THEN 'No warranty'
ELSE TO_CHAR(warranty_period)
END AS descr
FROM table_name
Which, for the sample data:
CREATE TABLE table_name (warranty_period) AS
SELECT INTERVAL '0-0' YEAR TO MONTH FROM DUAL UNION ALL
SELECT INTERVAL '0-1' YEAR TO MONTH FROM DUAL UNION ALL
SELECT INTERVAL '1-0' YEAR TO MONTH FROM DUAL UNION ALL
SELECT INTERVAL '1-2' YEAR TO MONTH FROM DUAL;
Outputs:
WARRANTY_PERIOD
DESCR
+00-00
No warranty
+00-01
+00-01
+01-00
+01-00
+01-02
+01-02
Or, if you want a different format then EXTRACT the component parts:
SELECT warranty_period,
CASE warranty_period
WHEN INTERVAL '0-0' YEAR TO MONTH
THEN 'No warranty'
ELSE EXTRACT(YEAR FROM warranty_period) || 'y ' || EXTRACT(MONTH FROM warranty_period) || 'm'
END AS descr
FROM table_name
Which outputs:
WARRANTY_PERIOD
DESCR
+00-00
No warranty
+00-01
0y 1m
+01-00
1y 0m
+01-02
1y 2m
or:
SELECT warranty_period,
CASE
WHEN warranty_period <= INTERVAL '0-0' YEAR TO MONTH
THEN 'No warranty'
WHEN warranty_period < INTERVAL '1-0' YEAR TO MONTH
THEN EXTRACT(MONTH FROM warranty_period) || ' months'
WHEN EXTRACT(MONTH FROM warranty_period) = 0
THEN EXTRACT(YEAR FROM warranty_period) || ' years'
ELSE EXTRACT(YEAR FROM warranty_period) || ' years and ' || EXTRACT(MONTH FROM warranty_period) || ' months'
END AS descr
FROM table_name
Outputs:
WARRANTY_PERIOD
DESCR
+00-00
No warranty
+00-01
1 months
+01-00
1 years
+01-02
1 years and 2 months
fiddle
Related
This question already has answers here:
How to get the previous working day from Oracle?
(4 answers)
Closed 1 year ago.
need help for some oracle stuff ..
I need to get Day-1 from sysdate, holiday and weekend will be excluded .
And for holiday, we need to get the range to get the last workday before holiday.
The start date and end date will coming from my holiday table.
ex :
Holiday Table
HolidayName
Start_date
End_Date
holiday1
5th Aug'21
6th Aug'21
condition :
this query run on 9th Aug 2021
expected result :
4th Aug'21
I've tried some query and function but I just can't get what I need.
Thanks a lot for help!
Here's one way to do it.
select max(d) as last_workday
from (select trunc(sysdate)-level as d from dual connect by level < 30) prior_month
where to_char(d, 'DY') not in ('SAT','SUN')
and not exists (select holidayname from holiday_table
where prior_month.d between start_date and end_date)
;
Without seeing your Holiday table, it's hard to say how many days back you would need to look to find the last workday. If you have a holiday that lasts for more than 30 days, you'll need to change the 30 to a larger number.
You can use a simple case expression to determine what day of the week the start of your holiday is, then subtract a number of days based on that.
WITH
holiday (holidayname, start_date, end_date)
AS
(SELECT 'holiday1', DATE '2021-8-5', DATE '2021-8-6' FROM DUAL
UNION ALL
SELECT 'Christmas', DATE '2021-12-25', DATE '2021-12-26' FROM DUAL
UNION ALL
SELECT 'July 4th', DATE '2021-7-4', DATE '2021-7-5' FROM DUAL)
SELECT holidayname,
start_date,
end_date,
start_date - CASE TO_CHAR (start_date, 'Dy') WHEN 'Mon' THEN 3 WHEN 'Sun' THEN 2 ELSE 1 END AS prior_business_day
FROM holiday;
HOLIDAYNAME START_DATE END_DATE PRIOR_BUSINESS_DAY
______________ _____________ ____________ _____________________
holiday1 05-AUG-21 06-AUG-21 04-AUG-21
Christmas 25-DEC-21 26-DEC-21 24-DEC-21
July 4th 04-JUL-21 05-JUL-21 02-JUL-21
You can use a recursive sub-query factoring clause from this answer:
WITH start_date (dt) AS (
SELECT DATE '2021-05-02' FROM DUAL
),
days ( dt, day, found ) AS (
SELECT dt,
TRUNC(dt) - TRUNC(dt, 'IW'),
0
FROM start_date
UNION ALL
SELECT dt - CASE day WHEN 0 THEN 3 WHEN 6 THEN 2 ELSE 1 END,
CASE WHEN day IN (0, 6, 5) THEN 4 ELSE day - 1 END,
CASE WHEN h.start_date IS NULL THEN 1 ELSE 0 END
FROM days d
LEFT OUTER JOIN holidays h
ON ( dt - CASE day WHEN 0 THEN 3 WHEN 6 THEN 2 ELSE 1 END
BETWEEN h.start_date AND h.end_date )
WHERE found = 0
)
SELECT dt
FROM days
WHERE found = 1;
Which, for the sample data:
CREATE TABLE holidays (HolidayName, Start_date, End_Date) AS
SELECT 'holiday1', DATE '2021-08-05', DATE '2021-08-06' FROM DUAL;
Outputs:
DT
2021-08-04 00:00:00
db<>fiddle here
Don't know if it's very efficient. Did it just for fun
create table holidays (
holiday_name varchar2(100) primary key,
start_date date not null,
end_date date not null
)
/
Table created
insert into holidays (holiday_name, start_date, end_date)
values ('holiday1', date '2021-08-05', date '2021-08-06');
1 row inserted
with days_before(day, wrk_day) as
(select trunc(sysdate - 1) d,
case
when h.holiday_name is not null then 0
when to_char(trunc(sysdate - 1), 'D') in ('6', '7') then 0
else 1
end work_day
from dual
left join holidays h
on trunc(sysdate - 1) between h.start_date and h.end_date
union all
select db.day - 1,
case
when h.holiday_name is not null then 0
when to_char(db.day - 1, 'D') in ('6', '7') then 0
else 1
end work_day
from days_before db
left join holidays h
on db.day - 1 between h.start_date and h.end_date
where db.wrk_day = 0) search depth first by day set order_no
select day from days_before where wrk_day = 1;
DAY
-----------
04.08.2021
i'm trying to get sysdate year and month into number or char.
I'd want something like this:
202107
or last month
202106
i tried this code:
select trunc(add_months(sysdate, -1), 'MM') from dual;
please help, thanks
Convert it to a string and then to a number:
SELECT TO_NUMBER(TO_CHAR(ADD_MONTHS(SYSDATE,-1), 'YYYYMM'))
FROM DUAL;
Or, you can use extract and wrap it in a sub-query so you do not need to repeat adding the months:
SELECT EXTRACT(YEAR FROM dt) * 100 + EXTRACT(MONTH FROM dt)
FROM (
SELECT ADD_MONTHS(SYSDATE, -1) AS dt
FROM DUAL
)
Like
SELECT EXTRACT(year from sysdate) * 100 + EXTRACT(month from sysdate)
If you want to scroll around, manipulate the date before you extract from it, rather than minusing after you extract (gets tricky to e.g. go back a month if the date is in jan)
--will work for jan 2021
SELECT EXTRACT(year from ADD_MONTHS(somedate, -1)) * 100 + EXTRACT(month from ADD_MONTHS(somedate, -1))
--won't work for jan 2021
SELECT EXTRACT(year from somedate) * 100 + (EXTRACT(month from somedate) - 1)
TRUNC is a device that "rounds" a date to a particular interval, such as "trimming the time off a datetime" or "making any day of the week back to the date that was the start of the week" - very useful for all sorts of stuff like "total sales by month - SUM(sale) GROUP BY TRUNC(saledate, 'mm')" but it keeps all the components of the date (the day, the hour, the minute etc) so it isn't what you want.
I am woking on a query which can give back the count divided by month about the offices that will be closed this summer.
SELECT
qa.tmonth,
COUNT(qa.tmonth) AS qtn
FROM
(
SELECT TO_CHAR(CLOSURE_DATE, 'yyyymm') AS tmonth
FROM Holidays
WHERE CLOSURE_DATE >= TO_DATE('20160501', 'YYYY-MM-DD') AND
CLOSURE_DATE <= TO_DATE('20160901', 'YYYY-MM-DD')
) qa
GROUP BY qa.tmonth;
Since the months: May, June, August and September no office will be closed the output is the following:
TMONTH|QTN
201607|80
But I need a thing like this
TMONTH|QTN
201605|0
201606|0
201607|80
201608|0
201609|0
How could I achieve that?
Thanks to all!
You can try with something like this:
SQL> with holidays(closure_date) as
2 (
3 select date '2016-07-01' from dual union all
4 select date '2016-07-02' from dual union all
5 select date '2016-07-03' from dual union all
6 select date '2016-07-04' from dual union all
7 select date '2016-07-05' from dual
8 )
9 select count(closure_date) as closure_days, to_char(day, 'yyyymm') as month
10 from (
11 select date '2016-05-01' + level -1 as day
12 from dual
13 connect by date '2016-05-01' + level -1 <= date '2016-09-30'
14 ) days
15 left outer join holidays
16 on (day = closure_date)
17 group by to_char(day, 'yyyymm') ;
CLOSURE_DAYS MONTH
------------ ------
0 201608
5 201607
0 201606
0 201605
0 201609
SQL>
This uses a query to build the list of all the days between a starting and an ending date; I used 01/05 and 30/09 and called it days.
Then it queries days with the holidays table in outer join; this way you can count only the days for which there is a corrensponding value in the closure days list, thus counting the closure days for each day, month year; the aggregation for year and month completes the job
A similar approach like above. Tip: You can execute the two sub-queries separately, to analyse the logic.
select to_char (m.month, 'yyyymm') as TMONTH, m.month,
nvl (h.qtn, 0) as QTN
from
(
SELECT add_months(trunc (SYSDATE, 'MONTH'), -(LEVEL-1)) as MONTH
FROM dual
CONNECT BY LEVEL <= 12 -- generate a list of the last 12 month
) m
left join
(
SELECT trunc (closure_date, 'MONTH') as MONTH,
count (*) as QTN
FROM Holidays
group by trunc (closure_date, 'MONTH')
) h
on m.MONTH = h.MONTH
where m.month between DATE '2016-01-01' and sysdate
order by TMONTH desc;
I have a table t_time where I have the below attributes
time_key, calendar_dt, cal_year, cal_quarter, cal_month, cal_week, week_in_month, cal_st_dt_of_wk, cal_end_dt_of_wk, rfrsh_dt, cal_yyyymm
select * from t_time where time_key = (select max(time_key) from t_time);
74937 31-12-2015 2015 4 12 5 5 27-12-2015 02-01-2016 17-07-2009 201512
I want to write a stored proc such that when i specify the year,t_time should be inserted with all the keys and other attributes..
like
for 2016
time_key calendar_dt cal_year cal_quarter cal_month cal_week week_in_month cal_st_dt_of_wk cal_end_dt_of_wk rfrsh_dt cal_yyyymm
74938 01-01-2016 2016 1 1 1 1 01-01-2016 02-01-2016 22-04-2015 201601
74939 02-01-2016 2016 1 1 1 1 01-01-2016 02-01-2016 22-04-2015 201601
74940 03-01-2016 2016 1 1 2 2 03-01-2016 09-01-2016 22-04-2015 201601
74941 04-01-2016 2016 1 1 2 2 03-01-2016 09-01-2016 22-04-2015 201601
cal_end_dt_of_wk is saturday of that week
cal_st_dt_of_wk is sunday of that week
can someone give me an idea to start with..
time_key - time_key + 1
calendar_dt - select sysdate from dual;
cal_year - select extract(year from sysdate) from dual;
cal_quarter - select case when extract(month from sysdate) in (1,2,3) then 1
case when extract(month from sysdate) in (4,5,6) then 2
case when extract(month from sysdate) in (7,8,9) then 3
case when extract(month from sysdate) in (10,11,12) then 4 else 0 end as cal_quarter from dual;
cal_month - select extract(month from sysdate) from dual;
cal_week - select to_char(to_date(sysdate),'ww') from dual;
week_in_month - select to_char(to_date(sysdate),'w') from dual;
cal_st_dt_of_wk - select trunc(sysdate,'iw')-1 from dual;
cal_end_dt_of_wk - select trunc(sysdate,'iw')+5 from dual;
rfrsh_dt - select sysdate from dual;
cal_yyyymm - select to_char(sysdate,'yyyymm') from dual;
Ok, it's a lot of stuff ;) but I think this should work now:
-- provide year as YYYY
CREATE OR REPLACE PROCEDURE fill_table (year IN VARCHAR2) IS
date_holder DATE := TO_DATE ('01.01.' || year,'DD.MM.YYYY');
BEGIN
WHILE (TO_CHAR (date_holder,'YYYY') = year) LOOP
INSERT INTO t_time
VALUES (1,
date_holder,
extract(year from date_holder),
case when extract(month from date_holder) in (1,2,3) then 1
when extract(month from date_holder) in (4,5,6) then 2
when extract(month from date_holder) in (7,8,9) then 3
when extract(month from date_holder) in (10,11,12) then 4 else 0 END,
extract(month from date_holder),
to_char(to_date(date_holder),'ww'),
to_char(to_date(date_holder),'w'),
trunc(date_holder,'iw')-1,
trunc(date_holder,'iw')+5,
sysdate ,
to_char(date_holder,'yyyymm'));
date_holder := date_holder +1;
END LOOP;
END;
/
So the basic idea is:
start with the 1.1.<YEAR> date
add one day by the other and insert values as you described
There seems to be an issue on the calendar week, week in month, start and end of week though .... anyway - the approach should be fine. I omitted the correct key calculation as well - best option would be a SEQUENCE.
p.s.: check this demo.
I need to make a view in Oracle that will display all rows from last weekday.
So for Tuesday it should be like this cause I need all entries from Monday:
select * from table_1 where to_char(Mydate,'yyyy-mm-dd') = to_char(sysdate-**1**,'yyyy-mm-dd');
But if it is Monday then I need all entries from Friday.
select * from table_1 where to_char(Mydate,'yyyy-mm-dd') = to_char(sysdate-**3**,'yyyy-mm-dd');
How can I make one view that will always display correct rows from last weekday?
SELECT *
FROM table_1
WHERE mydate >= SYSDATE - CASE WHEN TO_CHAR(SYSDATE, 'D') > 6 THEN 3 ELSE 1 END
AND mydate < SYSDATE - CASE WHEN TO_CHAR(SYSDATE, 'D') > 6 THEN 3 ELSE 1 END + 1
You could use CASE:
SQL> WITH table_1 AS
2 (SELECT SYSDATE - 20 + ROWNUM mydate FROM dual CONNECT BY ROWNUM <= 40)
3 SELECT *
4 FROM table_1
5 WHERE trunc(Mydate) =
6 CASE WHEN to_char(SYSDATE, 'D') = to_char(DATE '2000-01-02', 'D')
7 THEN -- sunday
8 trunc(SYSDATE - 2)
9 WHEN to_char(SYSDATE, 'D') = to_char(DATE '2000-01-03', 'D')
10 THEN -- monday
11 trunc(SYSDATE - 3)
12 ELSE
13 trunc(SYSDATE - 1)
14 END;
MYDATE
-----------
08/01/2010
Note: Depending upon your NLS setting, to_char(X, 'D') may return 1 for mondays or sundays. Relying on a known monday (eg: 2000-01-03 in my case) will make this query work in any setting.