Hi I am working on oracle DB. The DB has one column date. It consists of the dates of 5 years with the dates model will be refreshed. Ex
DATE_TABLE
DATE
------------
1-jan-2013
15-jan-2013
31-jan-2013
6-feb-2013
etc.........
now for today's date suppose 13th jan 2013. The next refresh date will be 15th jan. and previous refresh date is 1st jan. to retrieve these two dates. Can i have any way without using PL/SQL. using regular select queries?. Thanks in advance
There are two functions LAG() (allows you to reference previous record) and LEAD() allows you to reference next record. Here is an example:
SQL> with t1(col) as(
2 select '1-jan-2013' from dual union all
3 select '15-jan-2013' from dual union all
4 select '31-jan-2013' from dual union all
5 select '6-feb-2013' from dual
6 )
7 select col as current_value
8 , lag(col, 1) over(order by col) as prev_value
9 , lead(col, 1) over(order by col)as next_value
10 from t1
11 ;
Result:
CURRENT_VALUE PREV_VALUE NEXT_VALUE
------------- ----------- -----------
1-jan-2013 NULL 15-jan-2013
15-jan-2013 1-jan-2013 31-jan-2013
31-jan-2013 15-jan-2013 6-feb-2013
6-feb-2013 31-jan-2013 NULL
We can simply use the below query, plain and simple. No need of pl/sql
SELECT MIN(DATE) FROM DATE_TABLE WHERE DATE > SYSDATE ;
Related
How can I make index column start over after reaching 5th row? I can't do that with a window function as there are no groups, I just need an index with max number of 5 like this:
date
index
01.01.21
1
02.01.21
2
03.01.21
3
04.01.21
4
05.01.21
5
06.01.21
1
07.01.21
2
and so on.
Appreciate any ideas.
You can use below solution for that purpose.
First, rank (row_number analytic function)the rows in your table within inline view
Then, use again the row_number function with partition by clause to group the previously ranked rows by TRUNC((rnb - 1)/5)
SELECT t."DATE"
, row_number()over(PARTITION BY TRUNC((rnb - 1)/5) ORDER BY rnb) as "INDEX"
FROM (
select "DATE", row_number()OVER(ORDER BY "DATE") rnb
from Your_table
) t
ORDER BY 1
;
demo on db<>fiddle
Your comment about using analytic functions is wrong; you can use analytic functions even when there are no "groups" (or "partitions"). Here you do need an analytic function, to order the rows (even if you don't need to partition them).
Here is a very simple solution, using just row_number(). Note the with clause, which is not part of the solution; I included it just for testing. In your real-life case, remove the with clause, and use your actual table and column names. The use of mod(... , 5) is pretty much obvious; it looks a little odd (subtracting 1, taking the modulus, then adding 1) because in Oracle we seem to count from 1 in all cases, instead of the much more natural counting from 0 common in other languages (like C).
Note that both date and index are reserved keywords, which shouldn't be used as column names. I used one common way to address that - I added an underscore at the end.
alter session set nls_date_format = 'dd.mm.rr';
with
sample_inputs (date_) as (
select date '2021-01-01' from dual union all
select date '2021-01-02' from dual union all
select date '2021-01-03' from dual union all
select date '2021-01-04' from dual union all
select date '2021-01-05' from dual union all
select date '2021-01-06' from dual union all
select date '2021-01-07' from dual
)
select date_, 1 + mod(row_number() over (order by date_) - 1, 5) as index_
from sample_inputs
;
DATE_ INDEX_
-------- ----------
01.01.21 1
02.01.21 2
03.01.21 3
04.01.21 4
05.01.21 5
06.01.21 1
07.01.21 2
You can combine MOD() with ROW_NUMBER() to get the index you want. For example:
select date, 1 + mod(row_number() over(order by date) - 1, 5) as idx from t
[I have created a table as shown in figure
CREATE TABLE TABLE1 (CAL_YEAR VARCHAR2(4), NAME VARCHAR2(1))
INSERT INTO TABLE1 VALUES(‘2020’, ’A’)
INSERT INTO TABLE1 VALUES(‘2020’, ’B’)
INSERT INTO TABLE1 VALUES(‘2020’, ’C’)
INSERT INTO TABLE1 VALUES(‘2020’, ’D’)
INSERT INTO TABLE1 VALUES(‘2021’, ’E’)
INSERT INTO TABLE1 VALUES(‘2021’, ’F’)
Let us assume I am querying the statement in this year like
SELECT * FROM TABLE1 WHERE CAL_YEAR= TO_CHAR((SYSDATE),'YYYY');
so I get present year values.
What I need is if the present year data not present in the table then I need to use previous year data i.e. 2020 data...How can I do this in the where clause itself]1
You can simply get the maximum year that is less than or equal the year you query for in a subquery.
SELECT *
FROM table1
WHERE cal_year = (SELECT max(cal_year)
FROM table1
WHERE cal_year <= 2020);
You want to show the previous year, if no row for the current year exists? Simply select all years until now, order by year, take the last one.
SELECT *
FROM table1
WHERE cal_year <= EXTRACT(YEAR FROM SYSDATE)
ORDER BY cal_year DESC
FETCH FIRST ROW WITH TIES;
Optionally (if you're on a database version which doesn't support FETCH FIRST ROW WITH TIES Thorsten suggested):
with year 2021 in the table:
SQL> select * from table1
2 where cal_year = case when (select max(1) from table1
3 where exists (select null from table1
4 where cal_year = to_char(sysdate, 'yyyy')
5 )
6 ) = 1 then to_char(sysdate, 'yyyy')
7 else to_char(add_months(sysdate, -12), 'yyyy')
8 end;
CAL_ N
---- -
2021 E
2021 F
Without this year:
SQL> delete from table1 where cal_year = 2021;
2 rows deleted.
SQL>
SQL> select * from table1
2 where cal_year = case when (select max(1) from table1
3 where exists (select null from table1
4 where cal_year = to_char(sysdate, 'yyyy')
5 )
6 ) = 1 then to_char(sysdate, 'yyyy')
7 else to_char(add_months(sysdate, -12), 'yyyy')
8 end;
CAL_ N
---- -
2020 A
2020 B
2020 C
2020 D
SQL>
What does it do?
lines #2 - 5: SELECT* (within CASE) returns 1 if EXISTS says that there's at least one row whose CAL_YEAR = this year
if that's so (i.e. 1 has been returned), then CAL_YEAR is compared to this year (to_char(sysdate, 'yyyy'))
otherwise, if SELECT returns something else (most probably NULL), CAL_YEAR is compared to previous year (that's what to_char(add_months(sysdate, -12), 'yyyy') does - subtracts 12 months from today's date and extracts year from it)
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 want to migrate a table which contains some columns with dates. The issue is my dates are often in dd/mm/yyyyy HH24:MM:YYYY format. But sometimes it appears that the format is only dd/mm/yyyy, or blank.
I guess that's why I'm getting ORA-01830 when I'm trying to migrate the datas.
I tried
CASE
WHEN TO_DATE(MYDATE,'DD/MM/YYYY')
then TO_DATE(MYDATE,'DD/MM/YYYY 00:00:00')
END AS MYDATE
But I'm not sure if it is possible to test the date format (and ofcourse it's not working).
Thank you
TO_DATE cannot test date format, but you can do it. If Lalit's answer would not be enough, try something like
select
case when my_date like '__/__/__' then to_date(my_date, 'dd/mm/yy')
when my_date like '__-__-__' then to_date(my_date, 'dd-mm-yy')
...
end
So you have the data type issue. DATE is stored as string literal. As you have mentioned that the date model has the DD/MM/YYYY part same, just that the time portion is either missing for some rows or the entire value is NULL.
For example, let's say your table have the values like -
SQL> WITH dates AS(
2 SELECT 1 num, '29/12/2014 16:38:57' dt FROM dual UNION ALL
3 SELECT 2, '29/12/2014' FROM dual UNION ALL
4 SELECT 3, NULL FROM dual
5 )
6 SELECT num, dt
7 FROM dates
8 /
NUM DT
---------- -------------------
1 29/12/2014 16:38:57
2 29/12/2014
3
SQL>
TO_DATE with proper format model should do the trick.
Let's stick to a format model first.
SQL> alter session set nls_date_format='dd/mm/yyyy hh24:mi:ss';
Session altered.
Now, let's use TO_DATE to explicitly convert the string literal to date.
SQL> WITH dates AS(
2 SELECT 1 num, '29/12/2014 16:38:57' dt FROM dual UNION ALL
3 SELECT 2, '29/12/2014' FROM dual UNION ALL
4 SELECT 3, NULL FROM dual
5 )
6 SELECT num, to_date(dt, 'dd/mm/yyyy hh24:mi:ss') dt
7 FROM dates
8 /
NUM DT
---------- -------------------
1 29/12/2014 16:38:57
2 29/12/2014 00:00:00
3
SQL>
I have a date field
DATE = 10/10/2010
sum = 4 (this are number of years by calculation)
is there a way to add four years to 10/10/2010 and make it
10/10/2014?
Try adding months (12 * number of years) instead. Like this-
add_months(date'2010-10-10', 48)
Use add_months
Example:
SELECT add_months( to_date('10-OCT-2010'), 48 ) FROM DUAL;
Warning
add_months, returns the last day of the resulting month if you input the last day of a month to begin with.
So add_months(to_date('28-feb-2011'),12) will return 29-feb-2012 as a result.
I believe you could use the ADD_MONTHS() function. 4 years is 48 months, so:
add_months(DATE,48)
Here is some information on using the function:
http://www.techonthenet.com/oracle/functions/add_months.php
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1157035034361
You can try this:
someDate + interval '4' year
INTERVAL
I am not sure, if I understood Your question correctly, but
select add_months(someDate, numberOfYears * 12) from dual
might do the trick
One more option apart from ADD_MONTHS
SELECT
SYSDATE,
SYSDATE
+ TO_YMINTERVAL ( '1-0' )
FROM
DUAL;
SYSDATE SYSDATE+TO_YMINTERVAL('1-0')
--------- ----------------------------
29-OCT-13 29-OCT-14
1 row selected.
SELECT
SYSDATE,
SYSDATE
+ TO_YMINTERVAL ( '2-0' )
FROM
DUAL;
SYSDATE SYSDATE+TO_YMINTERVAL('2-0')
--------- ----------------------------
29-OCT-13 29-OCT-15
1 row selected.
SELECT
TO_DATE ( '29-FEB-2004',
'DD-MON-YYYY' )
+ TO_YMINTERVAL ( '1-0' )
FROM
DUAL
*
Error at line 4
ORA-01839: date not valid for month specified
But the last one is illegal since there is no 29th day of February in 2005, hence it fails on leap year cases (Feb 29)
Read the documentation for the same
SELECT TO_CHAR(SYSDATE,'YYYY')-2 ANO FROM DUAL