UPDATE month and year to current but leave day - sql

I have situation in Oracle DB where I need to UPDATE every month some dates in table following this condition:
1) If date in table like '03.06.2017' UPDATE to '03.11.2017'
2) If date in table like '29.06.2016' UPDATE to '29.11.2017'
2) If date in table like '15.02.2016' UPDATE to '15.11.2017'
So basically always UPDATE part of date(month, year) to current month/year but always leave day as it is.
Edit:
It will be all months from 1-12 not only June. I need to do something like this... UPDATE table SET date = xx.(month from sysdate).(year from sysdate) WHERE... xx (day) leave as it is in DB.
Br.

You can use MONTHS_BETWEEN to determine how many months you need to add and then use the ADD_MONTHS function:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE dates ( value ) AS
SELECT DATE '2017-06-03' FROM DUAL UNION ALL
SELECT DATE '2016-06-29' FROM DUAL UNION ALL
SELECT DATE '2016-02-15' FROM DUAL UNION ALL
SELECT DATE '2016-03-31' FROM DUAL;
Update:
UPDATE dates
SET value = ADD_MONTHS(
value,
CEIL( MONTHS_BETWEEN( TRUNC( SYSDATE, 'MM' ), value ) )
);
Query 1:
SELECT * FROM dates
Results:
| VALUE |
|----------------------|
| 2017-11-03T00:00:00Z |
| 2017-11-29T00:00:00Z |
| 2017-11-15T00:00:00Z |
| 2017-11-30T00:00:00Z | -- There are not 31 days in November

Probably you want
update your_table
set this_date = add_months(this_date, 5)
where ...
This will add five months to the selected dates.
Your edited question says you want to update all the dates to the current month and year; you can automate it like this ...
update your_table
set this_date = add_months(this_date,
months_between(trunc(sysdate,'mm'), trunc(this_date, 'mm')))
-- or whatever filter you require
where this_date between trunc(sysdate, 'yyyy') and sysdate
/
Using month_between() guarantees that you won't get invalid dates such as '2017-11-31'. You say in a comment that all the dates will be < 05.mm.yyyy but your sample data disagrees. Personally I'd go with a solution that doesn't run the risk of data integrity issues, because the state of your data tomorrow may will be different from its state today.
Check out the LiveSQL demo.

I would start off with something like this to get my dates and then craft an update from it (substitute old_date with your date column and source_table with the table name):
select old_date, to_char(sysdate, 'YYYY-MM-') || to_char(old_date, 'DD') from source_table;

Related

OR for two different columns

I am trying to select specific rows from an Oracle DB.
The table has the following structure:
Order
Date
Status
1
01.01.2018
10
2
01.01.2018
15
I would like to extract all rows where
Status = < 85 or
the order date is in this week
Unfortunately, column Status is declared as a text column.
How would you build a SQL to extract these specific rows?
Hmmm . . . I don't know what you mean by "this week". Perhaps:
where status <= '85' or
orderdate >= trunc(sysdate, 'IW')
This does a string comparison on the status, so '9' would not be matched, and uses the ISO definition of week for the current week.
If you want a numeric comparison for status, then:
where to_number(status) <= 85 or
orderdate >= trunc(sysdate, 'IW')
Maybe try the CAST function like
select* from yourtable where cast(Status as INT) <=85 AND
to_char(to_date(date,'MM/DD/YYYY'),'WW')=to_char(to_date(sysdate,'MM/DD/YYYY'),'WW')

Year in DD/MON/YY converts to 1943 and 2043 in 2 different tables with the same data

I am trying the same SQL on 2 different tables in the same database.
SELECT date_of_birth_1 from Table1 where id = '1111';
The output is 31/DEC/43.
SELECT date_of_birth_2 from Table2 where id = '1111';
The output is 31/DEC/43 again.
But when I run
SELECT extract(year from date_of_birth_1) from Table1 where id = '1111';
The output is 1943.
And when I run
SELECT extract(year from date_of_birth_2) from Table2 where id = '1111';
The output is 2043.
I don't understand what is going on, could you please help me. I want both the tables to use the same reference year which is 1900.
Edit: This happens only for some dates.
select EXTRACT(year FROM TO_DATE('01/AUG/43')) from dual;
The output is 2043.
select EXTRACT(year FROM TO_DATE('04/MAR/53')) from dual;
The output is 1953.
By default, Oracle is only showing the last two digits of the year. In one table, the date would seem to be 1943-12-31 and in the other 2043-12-31.
You can see the full date using to_char():
select to_char(dob, 'YYYY-MM-DD')
If you need to fix the data, you can do something like:
update t
set dob = add_months(dob, -12 * 100)
where dob > <whatever threshold you want here>

Displaying listagg values based on values in another column (part 2)

I posted a similar question previously here: Displaying LISTAGG values in a cell based on the value of another column.
In that question, I was using UPDATE to store a comma delimited list.
I got a solution for that but I realized thanks to the contributors, that this is not the best way to do so.
I am using Oracle APEX and need to display this data in a table.
ID Schedule Days
001 Weekend Saturday, Sunday
I have 3 tables to work with. They are, for simplicity, DAYS, SCHEDULES and DAY_SCHEDULES.
DAYS table
Day_ID - PK
Day_Order
Day_Name
Schedule (I added this column myself, the others were already provided)
SCHEDULES table
Schedule_ID - PK
Schedule (only two possible values: 'Weekend' or 'Weekday')
Days
Several other columns but not relevant
DAY_SCHEDULES table
Day_schedules_id - PK
Schedule_ID - FK
Day_ID - FK
I have tried several ways to make the Days column display a list of the corresponding days but none seem to work. My main attempts have been fiddling with SELECT/CASE statements but it still will not display anything even when the syntax is all correct.
I cannot make an FK between the two tables as neither Schedules column is unique.
I am not sure what to try next so any help or tips are appreciated!
Thank you,
Hassan
I think this is what you are trying to achieve (you do not need the DAYS column in the schedule table as you can calculate it from the days table):
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE DAYS ( Day_ID, Day_Order, Day_Name, Schedule ) AS
SELECT LEVEL,
LEVEL - 1,
TO_CHAR(
TRUNC( SYSDATE, 'IW' ) + LEVEL - 1,
'FMDay',
'NLS_LANGUAGE=ENGLISH'
),
CASE
WHEN LEVEL <= 5
THEN 'Weekday'
ELSE 'Weekend'
END
FROM DUAL
CONNECT BY LEVEL <= 7;
CREATE TABLE SCHEDULES ( Schedule_ID, Schedule, Other ) AS
SELECT 1, 'Weekend', 'ABC' FROM DUAL UNION ALL
SELECT 2, 'Weekday', 'DEF' FROM DUAL;
Query 1:
SELECT s.*,
d.days
FROM schedules s
INNER JOIN
( SELECT schedule,
LISTAGG( day_name, ', ' )
WITHIN GROUP ( ORDER BY day_order ) AS days
FROM days
GROUP BY schedule ) d
ON ( s.schedule = d.schedule )
Results:
| SCHEDULE_ID | SCHEDULE | OTHER | DAYS |
|-------------|----------|-------|----------------------------------------------|
| 2 | Weekday | DEF | Monday, Tuesday, Wednesday, Thursday, Friday |
| 1 | Weekend | ABC | Saturday, Sunday |

Oracle date extract and concatenate

I have to take a date (dd-mon-yyyy) and change the year based on a user input. How do I extract the day and month and add the year while keeping the format?
For example, it is currently 01-JAN-2015, and the user wants it to be 01-JAN-2016. I currently am doing:
SELECT EXTRACT(DAY FROM contract_year) || EXTRACT(MONTH FROM contract_year) || '2016'
FROM table
WHERE program = programid
And my output is 112016, but I want 01-JAN-2016. Any suggestions?
Try this
SELECT to_char(add_months('01-JAN-2015', 12*1),'dd-MON-yyyy') -- replace 1 with n where n = years
from dual
If you want to add use given year to current date/month, use this
with your_table as(
select '01-JAN-2015' as contract_year from dual
)
select to_date(substr(to_char(contract_year),1,7)||'2017','DD-MON-YYYY') from your_table
If your are not seeing output as required, it means that you have to change your client setting. Try running this in SQL Plus window.
Combine to_date and to_char:
SELECT to_date(to_char(contract_year,'dd-mm-') || '2016','dd-mm-yyyy')
FROM table
WHERE program = programid
It's not entirely clear to me whether contract_year is a date or a string.
If it's a date:
select to_char(contract_year, 'DD-MON-') || '2016'
from table
If it's a string in the format DD-MON-YYYY then:
select to_char(to_date(contract_year, 'DD-MON-YYYY'), 'DD-MON-') || '2016'
from table

SQL query for all the days of a month

i have the following table RENTAL(book_date, copy_id, member_id, title_id, act_ret_date, exp_ret_date). Where book_date shows the day the book was booked. I need to write a query that for every day of the month(so from 1-30 or from 1-29 or from 1-31 depending on month) it shows me the number of books booked.
i currently know how to show the number of books rented in the days that are in the table
select count(book_date), to_char(book_date,'DD')
from rental
group by to_char(book_date,'DD');
my questions are:
How do i show the rest of the days(if let's say for some reason in my database i have no books rented on 20th or 19th or multiple days) and put the number 0 there?
How do i show the number of days only of the current month so(28,29,30,31 all these 4 are possible depending on month or year)... i am lost . This must be done using only SQL query no pl/SQL or other stuff.
The following query would give you all days in the current month, in your case you can replace SYSDATE with your date column and join with this query to know how many for a given month
SELECT DT
FROM(
SELECT TRUNC (last_day(SYSDATE) - ROWNUM) dt
FROM DUAL CONNECT BY ROWNUM < 32
)
where DT >= trunc(sysdate,'mm')
The answer is to create a table like this:
table yearsmonthsdays (year varchar(4), month varchar(2), day varchar(2));
use any language you wish, e.g. iterate in java with Calendar.getInstance().getActualMaximum(Calendar.DAY_OF_MONTH) to get the last day of the month for as many years and months as you like, and fill that table with the year, month and days from 1 to last day of month of your result.
you'd get something like:
insert into yearsmonthsdays ('1995','02','01');
insert into yearsmonthsdays ('1995','02','02');
...
insert into yearsmonthsdays ('1995','02','28'); /* non-leap year */
...
insert into yearsmonthsdays ('1996','02','01');
insert into yearsmonthsdays ('1996','02','02');
...
insert into yearsmonthsdays ('1996','02','28');
insert into yearsmonthsdays ('1996','02','29'); /* leap year */
...
and so on.
Once you have this table done, your work is almost finished. Make an outer left join between your table and this table, joining year, month and day together, and when no lines appear, the count will be zero as you wish. Without using programming, this is your best bet.
In oracle, you can query from dual and use the conncect by level syntax to generate a series of rows - in your case, dates. From there on, it's just a matter of deciding what dates you want to display (in my example I used all the dates from 2014) and joining on your table:
SELECT all_date, COALESCE (cnt, 0)
FROM (SELECT to_date('01/01/2014', 'dd/mm/yyyy') + rownum - 1 AS all_date
FROM dual
CONNECT BY LEVEL <= 365) d
LEFT JOIN (SELECT TRUNC(book_date), COUNT(book_date) AS cnt
FROM rental
GROUP BY book_date) r ON d.all_date = TRUNC(r.book_date)
There's no need to get ROWNUM involved ... you can just use LEVEL in the CONNECT BY:
WITH d1 AS (
SELECT TRUNC(SYSDATE, 'MONTH') - 1 + LEVEL AS book_date
FROM dual
CONNECT BY TRUNC(SYSDATE, 'MONTH') - 1 + LEVEL <= LAST_DAY(SYSDATE)
)
SELECT TRUNC(d1.book_date), COUNT(r.book_date)
FROM d1 LEFT JOIN rental r
ON TRUNC(d1.book_date) = TRUNC(r.book_date)
GROUP BY TRUNC(d1.book_date);
Simply replace SYSDATE with a date in the month you're targeting for results.
All days of the month based on current date
select trunc(sysdate) - (to_number(to_char(sysdate,'DD')) - 1)+level-1 x from dual connect by level <= TO_CHAR(LAST_DAY(sysdate),'DD')
It did works to me:
SELECT DT
FROM (SELECT TRUNC(LAST_DAY(SYSDATE) - (CASE WHEN ROWNUM=1 THEN 0 ELSE ROWNUM-1 END)) DT
FROM DUAL
CONNECT BY ROWNUM <= 32)
WHERE DT >= TRUNC(SYSDATE, 'MM')
In Oracle SQL the query must look like this to not miss the last day of month:
SELECT DT
FROM(
SELECT trunc(add_months(sysdate, 1),'MM')- ROWNUM dt
FROM DUAL CONNECT BY ROWNUM < 32
)
where DT >= trunc(sysdate,'mm')