Add number of days in Oracle DB using SQL in java code - java

I am using below SQL in java code to update date in Oracle DB.
UPDATE CUS_LOGS SET START_DATE=to_date(systimestamp + 3,'DD-MON-RRRR'), END_DATE=to_date(systimestamp + 21921,'DD-MON-RRRR')
WHERE CUS_ID IN ('9b90cb8175ba0ca60175ba12d8711006');
My expectation is:
START_DATE = 05-NOV-2022
END_DATE = 08-NOV-2082.
However the result I see as
START_DATE = 05-NOV-2022
END_DATE = 08-NOV-1982 **********
Apparently issue happens when the date pass 31-12-2049. i.e. adding + 9922 as of today (02/11/2022)

The problem is that you're relying on implicit conversion, which is using your NLS settings, which are formatting dates as string with 2-digit years.
If your NLS_DATE_FORMAT is DD-MON-RR or DD-MON-YY then doing
to_date(systimestamp + 3,'DD-MON-RRRR')
is really doing something like:
to_date(to_char(cast(systimestamp as date) + 3,'DD-MON-RR','DD-MON-RRRR')
which means it tries to convert the string '08-NOV-82' to a date using the mask 'DD-MON-RRRR'. The RRRR means that a 2-digit year like 82 is interpreted as 1982 - which is what you're seeing. Using DD-MON-YYYY would be even worse, as that would interpret 82 as 0082.
alter session set nls_date_format = 'DD-MON-RR'
select
to_date(sysdate + 3,'DD-MON-RRRR') as a,
to_char(to_date(sysdate + 3,'DD-MON-RRRR'), 'YYYY-MM-DD') as b,
to_date(sysdate + 21921,'DD-MON-RRRR') c,
to_char(to_date(sysdate + 21921,'DD-MON-RRRR'), 'YYYY-MM-DD') as d,
to_date(sysdate + 3,'DD-MON-YYYY') as e,
to_char(to_date(sysdate + 3,'DD-MON-YYYY'), 'YYYY-MM-DD') as f,
to_date(sysdate + 21921,'DD-MON-YYYY') as g,
to_char(to_date(sysdate + 21921,'DD-MON-YYYY'), 'YYYY-MM-DD') as h
from dual
A
B
C
D
E
F
G
H
05-NOV-22
2022-11-05
08-NOV-82
1982-11-08
05-NOV-22
0022-11-05
08-NOV-82
0082-11-08
So you would need to explicitly convert the date to a string with a 4-digit year, then convert it back again...
But you don't need to do any of that conversion. Just adding the number of days to the timestamp - which implicitly converts that to a date, but safely - gets the result you want:
alter session set nls_date_format = 'YYYY-MM-DD'
alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS TZR'
select systimestamp,
systimestamp + 3,
systimestamp + 21921
from dual
SYSTIMESTAMP
SYSTIMESTAMP+3
SYSTIMESTAMP+21921
2022-11-02 10:42:24 +00:00
2022-11-05
2082-11-08
There's no point using systimestamp here though, you can avoid even that implicit conversion by using sysdate:
select sysdate,
sysdate + 3,
sysdate + 21921
from dual
SYSDATE
SYSDATE+3
SYSDATE+21921
2022-11-02
2022-11-05
2082-11-08
fiddle
You seem to be doing the conversion in order to remove the time portion of the current date/time - or, more correctly, set it to midnight as a date always has a time. But you can do that with trunc().
So, in summary, your update statement should just be:
UPDATE CUS_LOGS SET START_DATE=trunc(sysdate) + 3, END_DATE=trunc(sysdate) + 21921
WHERE CUS_ID IN ('9b90cb8175ba0ca60175ba12d8711006');
Your end date is roughly 60 years ahead; you might want to use add_months(trunc(sysdate), 60*12) and then add any additional days to that - maybe just keeping the + 3, inside or outside the add_months(), or both; add_months(trunc(sysdate) + 3, 60*12) + 3 would give the same result as + 21921, today anyway. But it isn't clear exactly what you're doing...
And you can add intervals rather than a number of days, which is a bit more explicit; but that works less well for adding months/years because it can lead to invalid-date errors, e.g. if you try to add a year interval to Feb 29th.

You can simplify the query and prevent issues from implicit timestamp-to-date-to-string conversions (as described by #Alex Poole) using:
UPDATE CUS_LOGS
SET START_DATE = TRUNC(SYSDATE) + INTERVAL '3' DAY,
END_DATE = ADD_MONTHS(TRUNC(SYSDATE), 60*12) + INTERVAL '6' DAY
WHERE CUS_ID = '9b90cb8175ba0ca60175ba12d8711006';

Related

JPA Repository : Cannot bind parameter for timezone

I have the following code in JPA repository and it works.
#Query(
value =
"SELECT EXTRACT(HOUR FROM STO.createddate\\:\\:timestamptz at time zone 'Asia/Seoul') AS hour,\n"
+ " COUNT(STO.id) AS count, SUM(STO.grandtotalprice) AS sum, AVG(STO.grandtotalprice) AS average\n"
+ "FROM store.storeorder AS STO\n"
+ "WHERE STO.store_id=?1 AND STO.createddate >= ?2 AND STO.createddate < ?3 AND STO.orderstatus IN ('CLOSED')\n"
+ "GROUP BY EXTRACT(HOUR FROM STO.createddate\\:\\:timestamptz at time zone 'Asia/Seoul') \n"
+ "ORDER BY hour ASC;",
nativeQuery = true)
List<ReportHourly> hourlyReport(
UUID storeId, LocalDateTime from, LocalDateTime to);
However, when I try to input timezone as parameter like below, it fails with saying
org.postgresql.util.PSQLException: ERROR: column "createddate" must appear in the GROUP BY clause or be used in an aggregate function
#Query(
value =
"SELECT EXTRACT(HOUR FROM STO.createddate\\:\\:timestamptz at time zone ?4) AS hour,\n"
+ " COUNT(STO.id) AS count, SUM(STO.grandtotalprice) AS sum, AVG(STO.grandtotalprice) AS average\n"
+ "FROM store.storeorder AS STO\n"
+ "WHERE STO.store_id=?1 AND STO.createddate >= ?2 AND STO.createddate < ?3 AND STO.orderstatus IN ('CLOSED')\n"
+ "GROUP BY EXTRACT(HOUR FROM STO.createddate\\:\\:timestamptz at time zone ?4) \n"
+ "ORDER BY hour ASC;",
nativeQuery = true)
List<ReportHourly> hourlyReport(
UUID storeId, LocalDateTime from, LocalDateTime to, String timeZone);
I am not sure why parameterization doesn't work for this case.
I think the problem here is that JDBC doesn't really know about indexed bind arguments, it only knows about ? for binding.
This means the two occurrences fo ?4 get translated into two different bind parameters and therefore Postgres sees an expression in the select clause that is not part of the GROUP BY nor an aggregate function.
Since by construction the two actually are the same you should be fine wrapping the expression for hour in MAX(...) or any other aggregate function that returns the argument value when applied to a single row.

SQL time value between 0:00 and 00:59 set to 23:xx

I read the time of some employees postings from my database table. Time is stored as time (without timezone).
Table
CREATE TABLE "dba"."mytable" (
"id" INTEGER NOT NULL DEFAULT AUTOINCREMENT UNIQUE,
"employeenr" CHAR(7 CHAR) NOT NULL,
"time" TIME NOT NULL
PRIMARY KEY ( "id" ASC)
) IN "system";
Table data looks like
id time employeenr
1 09:35 123
2 10:00 123
3 00:30 123
Reading any data with time between 0:00 and 00:59 from my application results in the time value being 23:xx.
Reading it by using SQL central gives me the correct time values.
Application is running on Tomcat 8, Database is SQL Anywhere 17
The issue occurs when using Hibernate (hql / native SQL query) as well as java.sql.
String sql = "select * from mytable order by time";
Statement st = c.createStatement();
ResultSet result = st.executeQuery(sql);
while (result.next()) {
String nr = result.getString("employeenr");
Time time = result.getTime("time");
Date date = result.getDate("time");
System.out.println(nr + " " + time + " / " + date);
}
Result
123 23:30:00 / 0001-01-01
123 09:35:00 / 0001-01-01
123 10:00:00 / 0001-01-01

Sqlite group dates by day

I'm trying to retrieve a list of different days from table. They are stored in integer.
cal.getTime().getTime()
Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Date object.
What I've written
select strftime('%d-%m-%Y'," + date_clmn + ") from "
+ TABLE_NAME +" group by strftime('%d-%m-%Y'," + date_clmn + ")"
Next, I convert these values toString.
What I get:
24-11--471
And after that when I try to make query with
strftime('%d-%m-%Y',date_clmn)=24-11--471
I get nothing.
What's wrong?
Will I run in trouble with timezone using this way?
If there are some other ways make it, I'll be glad to hear you.
Thanks in advance.
UPDATE
Tried "date(" + date_clmn+ ",'unixepoch')"
now retrieved value is "1970-01-01"
Since you are not storing a date in your table, you need to change your field. Try with this code:
"select strftime('%d-%m-%Y'," + date_clmn + "/1000,'unixepoch') from "
+ TABLE_NAME +" group by strftime('%d-%m-%Y'," + date_clmn + "/1000,'unixepoch')"
I'm adding this because I think it could be helpful to someone. As per documentation strftime() returns a string date in a given format. So in this clause:
strftime('%d-%m-%Y',date_clmn) = 24-11--471
^ ^
string this expression returns an integer
There's a type mismatch here that SQLite doesn't detect. Even query will run perfectly (I tested it and runs without errors), but of course it won't retrieve any row.
The right comparisson would be:
strftime('%d-%m-%Y',date_clmn) = '24-11--471'
Testing: Here's a little piece of code to test this behavior:
CREATE TABLE dateTest(myDate VARCHAR);
INSERT INTO dateTest(myDate) VALUES ('2013-08-29 18:57:00');
SELECT strftime('%Y-%m-%d',myDate) AS formatted, myDate FROM dateTest WHERE strftime('%Y-%m-%d') = 2013-08-29; /* this won't retrieve any row */
SELECT strftime('%Y-%m-%d',myDate) AS formatted, myDate FROM dateTest WHERE strftime('%Y-%m-%d') = '2013-08-29'; /* this will retrieve '2013-08-29 | '2013-08-29 18:57:00' */
And here's the output:

Is it possible to call DATE_ADD() in Java and store the value in a temporary date variable?

I have a doubt about adding days to Date in MySQL database.
The following is my code:
res=stat.executeQuery("select st_date from tmp1 where st_date = '"+t1.getText()+"'");
while(res.next())
{
System.out.println(res.getDate(1));
int i=0;
while(i<14)
{
statement.executeUpdate("Insert into datetab values(DATE_ADD('"
+res.getDate("st_date")+"',INTERVAL 1 DAY),'"+tempname+"')");
i=i+1;
}
}
All the updates in datetab table occur, but there is a problem. I will explain the problem with an example. If the date from tmp1 table is 28-12-2000, then after executing the insert query with date_add(), what happens is that 13 new inserts are happening, but all those inserts are "29-12-2000".
If the date from tmp1 table is 28-12-2000 Then after executing the insert query with date_add(), what happens is that 13 new inserts are happening, but all those inserts are "29-12-2000".
Because that is exactly what you are asking for. Your insert statement is:
"Insert into datetab values(DATE_ADD('" + res.getDate("st_date") +
"',INTERVAL 1 DAY),'" + tempname + "')"
Since read.getDate is not changing in the loop, the same value is inserted in every interation.
Instead of "Interval 1 DAY", use "Interval " + i + " Day" should insert different days. Is that what you are looking for?

Optimizing MySQL update query

This is currently my MySQL UPDATE query, which is called from program written in Java:
String query = "UPDATE maxday SET DatePressureREL = (SELECT " +
"Date FROM ws3600 WHERE PressureREL = (SELECT MAX" +
"(PressureREL) FROM ws3600 WHERE Date >= '" + Date +
"') AND Date >= '" + Date + "' ORDER BY Date DESC LIMIT 1), " +
"PressureREL = (SELECT PressureREL FROM ws3600 WHERE " +
"PressureREL = (SELECT MAX(PressureREL) FROM ws3600 " +
"WHERE Date >= '" + Date + "') AND Date >= '" + Date +
"' ORDER BY Date DESC LIMIT 1), ...";
try {
s.execute(query);
}
catch (SQLException e) {
System.out.println("SQL error");
}
catch(Exception e) {
e.printStackTrace();
}
Let me explain first, what does it do. I have two tables, first is ws3600, which holds columns (Date, PressureREL, TemperatureOUT, Dewpoint, ...). Then I have second table, called maxday, which holds columns like DatePressureREL, PressureREL, DateTemperatureOUT, TemperatureOUT,... Now as you can see from an example, I update each column, the question is, is there a faster way? I am asking this, because I am calling MAX twice, first to find the Date for that value and secondly to find the actual value. Now I know that I could write like that:
SELECT Date, PressureREL FROM ws3600 WHERE PressureREL =
(SELECT MAX(PressureREL) FROM ws3600 WHERE Date >= '" +
Date + "') AND Date >= '" + Date + "'
ORDER BY Date DESC LIMIT 1
That way I get the Date of the max and the max value at the same time and then update with those values the data in maxday table. But the problem of this solution is, that I have to execute many queries, which as I understand takes alot more time compared to executing one long mysql query because of overhead in sending each query to the server.
If there is no better way, which solution beetwen this two should I choose. The first, which only takes one query but is very unoptimized or the second which is beter in terms of optimization, but needs alot more queries which probably means that the preformance gain is lost because of overhead in sending each query to the server?
Doing 2 queries isn't really a problem for me, but they should be in a transaction (the reads and the write), this way you'll be sure that your update values are not wrong. With one query you do not have this problem.
I think the time lost in reading some data is nothing regarding the time lost by performing a write operation. A write operation is not by definition a fast thing, you could have triggers, you're maybe emptying the query cache from all requests impacting this table, the database needs to sync your write on disk, etc.
The more important thing for you is to keep your process simple, readable, and logic.
1) I think the problem goes deeper than just SQL optimization. Do you think this could be modeled differently where you don't have to migrate data like this (this much, and this often too btw) in the first place? Perhaps just using a FK/cross table to link the two together instead of migrating every field?
2) One query is much better than using JDBC to constantly go back and forth over the connection with new statements. That is a very expensive operation (each time). You will always want to stick to condensing queries into one as opposed to using iteration to make execute many statements.
Working from the inside out, it looks like all your subqueries do the same thing.
What's the point of having a where clause that does Date >= '" + Date + "') AND Date >= '" + Date + "' ?
Without going into column names or technical details, what are the purposes of your two tables?
String query = #"UPDATE maxday SET DatePressureREL = (SELECT Date FROM ws3600 WHERE PressureREL = (SELECT MAX(PressureREL) FROM ws3600 WHERE Date >= #Date) AND Date >= #Date ORDER BY Date DESC LIMIT 1), PressureREL = (SELECT PressureREL FROM ws3600 WHERE PressureREL = (SELECT MAX(PressureREL) FROM ws3600 WHERE Date >= #Date) AND Date >= #Date ORDER BY Date DESC LIMIT 1), ...";
After this, ideally if you were using a SelectCommand of some type instead of a string, you would
query.Parameters.Add(new MySqlParameter("#Date", yourdate));
Alternatively, you can just do this, although it opens you up to sql injection
query = query.replace("#Date", "'" + Date "'");
Either way, it makes the query considerably more legible.
If you can get all the values in one select query, this might work. Use a stored procedure accepting one parameter (date) that does:
One select statement, storing the values in a cursor, and
One update statement, using the values in the cursor.
Cursor Example

Categories