Oracle sql not reading the entire string - sql

I have a oracle sql query to get me the dates from 18/05/2021 to '28/05/2021'.
For some reason the the value after slash is not read, as the values from month 4 is also outputed. I dont know where i am wrong. Please give a hand if you are able, thanks a lot for your time.
NOTE: the dates have been stored in the database with a varchar datatype.
SELECT datadate
FROM mytable
WHERE trailerid= '1' and datattime>'05:00:00' and datattime<'12:00:00'
AND datadate between '18/05/2021' and '28/05/2021'
GROUP BY datadate ORDER BY datadate;
Current output
DATADATE
----------------
18/05/2021
19/04/2021
19/05/2021
20/05/2021
21/04/2021
21/05/2021
22/04/2021
22/05/2021
23/04/2021
23/05/2021
24/04/2021
24/05/2021
25/04/2021
25/05/2021
26/04/2021
26/05/2021
27/04/2021
27/05/2021
28/04/2021
28/05/2021

That's what happens when people store date values as strings.
See if this helps:
AND to_date(datadate, 'dd/mm/yyyy') between to_date('18/05/2021', 'dd/mm/yyyy')
and to_date('28/05/2021', 'dd/mm/yyyy')

You don't have to start over, just move the data to a datetime column
ALTER TABLE t ADD x DATE;
UPDATE t SET x = to_date(concat(datadate,datatime), 'dd/mm/yyyyhh24:mi:ss'))
WHERE datadate in (SELECT to_char(to_date('1999-12-31', 'yyyy-mm-dd') + level, 'dd/mm/yyyy') FROM dual CONNECT BY level <= 10000)
That WH ERE clause should generate a list of valid dates from 2000 to about 2030 - if you have dates outside this range adjust accordingly
Should now be able to find invalid dates (they weren't part of the where clause and x should hence remain null) and fix manually:
SELECT * FROM t WHERE x is null
Then drop your datadate and datatime cols and rename x to datadatetime
Now queries like BETWEEN work properly, and if you need just the date part you can do TRUNC(x). (You can even TRUNC to other date parts like hours, to cut the minutes and seconds off, or week of year to round dates down to the start of the week etc)
If you need just the time you either do x - TRUNC(x) which gives a decimal number like 0.5 for 12 noon or 0.75 for 6pm, or you can TOCHAR depending on what you want to do. It would be better to do x -TRUNC(x) BETWEEN 9.0/24.0 AND 17.0/24.0 than doing a string compare

Related

(Oracle)Getting 30 days before

My goal is to get 30 days before.
The data are stored as varchar2 type, '20200630' and '20200613'.
The expected result is '20200531' and '20200514' for each.
I have simply subtracted 30 from my varchar2 '20200630' and '20200613' and
it shows result by casting, but the result are not expected such as '20200600','20200583' which are not form of date.
Could I know how to modify my code as below
WITH A AS
(SELECT '20200630' YEARMONTHDAY FROM DUAL
UNION ALL
SELECT '20200613' FROM DUAL)
SELECT YEARMONTHDAY - 30 FROM A;
Store your data using the appropriate type! That is, use date rather than a string.
If you are stuck with data in this format, convert to date:
select to_date(yearmonthday, 'YYYYMMDD') - interval '30' day
I would not recommend converting the value back to a string. Dates should be dates.

Create a datetime value from separate date time columns

I have individual columns for year, month, day, hour, minute, second and millisecond. I need to combine them all together into a date timestamp. I am able to make a date value, but can't seem to make a valid time one. I am working in Oracle and I have sample below. I'd like my value to look like the createtime column.
This did the trick:
to_timestamp(XT.MONTH1||'-'||XT.DAY1||'-'||XT.YEAR||' '|| xt.hour24||'.'||xt.minute||'.'||xt.second||'.'||xt.milliseconds,'MM-DD-YYYY HH24:MI:SS.FF')
That is quite easy but not pretty. You convert the columns from datatype NUMBER to VARCHAR2 padded with leading zeros, using fm to suppress any spaces for + or -:
SELECT TO_CHAR(month,'fm00') FROM mytable;
05
Next, you glue the converted columns together with the || operator to get a single, long, string:
SELECT TO_CHAR(year,'fm0000')||TO_CHAR(month,'fm00')|| ...
20200519...
This long string can now be converted to datatype DATE, or, in your case TIMESTAMP as you have milliseconds. You need to specify the date format you used, f.i. 'YYYYMMDD...'
SELECT TO_TIMESTAMP(TO_CHAR(year,'fm0000')|| ... , 'YYYYMM')
A complete example looks like:
SELECT TO_TIMESTAMP(
TO_CHAR(year,'fm0000')||TO_CHAR(month,'fm00')||
TO_CHAR(day,'fm00')||TO_CHAR(hour24,'fm00')||
TO_CHAR(minute,'fm00')||TO_CHAR(second,'fm00')||
TO_CHAR(ms,'fm000')
, 'YYYYMMDDHH24MISSFF3')
FROM (-- your table, as a mockup, I'll use DUAL
SELECT 2020 as year, 5 as month, 19 as day,
13 as hour24, 7 as minute, 10 as second,
300 as ms
FROM DUAL);
2020-05-19 13:07:10,300000000
EDIT:
The fill mode modifier fm supresses a leading space for positive numbers (to make room for the - sign for negative numbers). All parts of a date are positive, so you get a lot of spaces in your string.
SELECT TO_CHAR(x,'99'), TO_CHAR(x,'fm99')
FROM (SELECT -10 as x FROM DUAL UNION ALL SELECT 10 FROM DUAL);
| 10|10|
|-10|-10|
The documentation is a bit hidden under Format Model Modifiers.
Come to think of it, you might as well keep the spaces and adjust your format model:
SELECT TO_TIMESTAMP(
TO_CHAR(year,'0000')||TO_CHAR(month,'00')||
TO_CHAR(day,'00')||TO_CHAR(hour24,'00')||
TO_CHAR(minute,'00')||TO_CHAR(second,'00')||
TO_CHAR(ms,'000')
,' YYYY MM DD HH24 MI SS FF3')
FROm (SELECT 2020 as year, 5 as month, 19 as day,
13 as hour24, 7 as minute, 10 as second,
300 as ms FROM DUAL);

Rolling 12 months oracle

I am writing a query in Oracle that I want to automate. I currently have a where clause that has the following:
TRAN.DATE BETWEEN 20160101 AND 20161231
I want the query to be a 12 month look back from the current date. I think I can do it if the date was in a date format using the following:
TRAN.DATE BETWEEN ADD_MONTHS(SYSDATE, -12) AND SYSDATE
But the date format in the table is in an integer format. I have tried to the TO_DATE function but I can't get the format right.
Yikes, you mean your database table literally has an integer of 20170104 (twenty million, one hundred seventy thousand, one hundred and four) and this represents the forth of Jan, this year?
Setting aside any concerns I have about this not being the best way to store a date, it's probably going to be easiest to write and also understand, if you convert your date to char and then to int:
WHERE DATECOLUMN BETWEEN
TO_NUMBER(TO_CHAR(ADD_MONTHS(SYSDATE,-12),'YYYYMMDD')) AND
TO_NUMBER(TO_CHAR(SYSDATE,'YYYYMMDD'))
There's no point getting clever and trying to deal with it as an integer math problem by subtracting 1200 or 10000 etc as a number because it'll make your query hard to understand and have no performance gain at all over a couple of number conversions as per this way
You'd be better off changing those dates to actual dates right now, before the codebase grows any bigger and someone decides it's a good idea to add more date columns stored as ints
You could try
TO_DATE(TRAN.DATE, 'YYYYMMDD') BETWEEN ADD_MONTHS(SYSDATE, -12) AND SYSDATE
According to Oracle documentation,
TO_DATE converts char of CHAR, VARCHAR2, NCHAR, or NVARCHAR2
datatype to a value of DATE datatype
so you need to first cast integer column to char or any of the accepted datatype specified above, and then cast it to DATE as below.
SELECT TO_DATE(to_char(20160101),'YYYYMMDD') as DATE1 FROM DUAL
Result
DATE1
-------------------
01.01.2016 00:00:00
so you need to rewrite your query as below.
TO_DATE(to_char(TRAN.DATE),'YYYYMMDD') BETWEEN ADD_MONTHS(SYSDATE, -12) AND SYSDATE
Also if you need only date part, you amy also need to trunc sysdate.
You can check the demo here
Hope this will help.

Update query with compare of date and time

I have a query to update some field on some condition.
Conditions
The time difference is not more than 1 hour and the date can be same.
select *
from Table
where user_cd = 'HARSHIT'
and to_char(sysdate, 'dd/mm/yyyy') = to_char(brth_dt, 'dd/mm/yyyy');
But one condition is also there like at night the user tries to update at 23:30 and after that the he tries next day at 00:15 so the difference is 45 min but the update must execute
select brth_dt from Table where user_cd = 'HARSHIT';
select sysdate from dual;
select brth_dt from Table
where user_cd = 'HARSHIT'
and sysdate-(1/24) < BRTH_DT;
Result of above query
BRTH_DT
25/02/2016 12:30:00
1 row selected.
SYSDATE
24/02/2016 16:7:58
1 row selected.
BRTH_DT
25/02/2016 12:30:00
1 row selected.
I see no reason to convert a date to a string ... if you need to check 2 dates are within an hour of each other, just do the math on the date, and compare :
select * from sir_people
where user_cd = 'HARSHIT'
and BRTH_DT BETWEEN sysdate-(1/24)
AND sysdate;
to_char on a date, for purposes of comparisons, is fundamentally flawed logic and should be avoided.
[edit] based on example provided: it appears you want to exclude future dates, and only include those dates between now, and an hour earlier.
query updated to accomodate that additional requirement.
to_char(col_name, 'yyyy-mm-dd hh24:mi:ss')
just use 24-hour format, I think that should do the work.
Simply translate required condition into sql:
"The time difference is not more than 1 hour and the date can be same."
select *
from Table
where user_cd = 'HARSHIT'
and abs(sysdate-brth_dt) <= 1/24

Oracle to_date function with quarter-format

I need to find some records created in a range of quarters. For example, I'm looking for all records created between the 4th quarter of 2008 and the 1st quarter of 2010. I have this in my WHERE-clause:
...and r.record_create_date between to_date('2008 4','YYYY Q')
and to_date('2010 1','YYYY Q')
but Oracle says: ORA-01820: format code cannot appear in date input format. The Q is a valid date format symbol, so I'm not sure what's happened. Is this even a valid way to find values in between calender quarters, or is there a better way?
Also interesting, and possibly related, if I execute this:
select to_date('2009','YYYY') from dual;
The value displayed in my IDE is 2009-08-01. I would have expected 2009-08-04, since today is 2010-08-04.
This:
select to_date('2009 1','YYYY Q') from dual;
of course, fails.
(Oracle 10g)
Oracle says: ORA-01820: format code cannot appear in date input format. The Q is a valid date format symbol, so I'm not sure what's happened.
See the second column of table 2.15 at http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34948. Not all format elements are allowed when converting to dates, timestamps, etc.
I recommend against using between for date range checks. People often will miss values within the ending day that the expect to be included. So I would translate:
and r.record_create_date between to_date('2008 4','YYYY Q')
and to_date('2010 1','YYYY Q')
To
and to_date('2008-10-01', 'YYYY-MM-DD') <= r.record_create_date
and record_create_date < to_date('2010-04-01', 'YYYY-MM-DD') -- < beginning of 2Q2010.
Someone asked the same question on OTN: http://forums.oracle.com/forums/thread.jspa?threadID=1081398&tstart=255
The crux of the issue is that you can not specify "Q" in the TO_DATE function.
Given that you're already specifying a portion of the date, why not provide the entire date? Mind too that to_date('2010 1','YYYY Q') would give you Jan 1st, 2010 when you really want March 31st, 2010... at a second to midnight.
Since the relationship between quarters to months is one-to-many, it doesn't make sense to do TO_DATE('2008 1', 'yyyy q'); what date should be returned? The first of the quarter, the end of the quarter, ...? (On the other hand, converting a date to a quarter - like TO_CHAR(SYSDATE, 'yyyy q') makes sense because a specific date only exists in one quarter.)
So, if you do want a query that looks for a date that falls between two quarters, you will have to "rolll your own" (explicitly stating the dates of the start/end of a quarter.)
As a side note, in case anyone is considering not using TO_DATE please do not use things like: WHERE date_value BETWEEN 'date string1' and 'date string2' without the TO_DATE function. It assumes a default date format and under certain situations can avoid potentially useful indexes altogether.
Below is one example where the same query can have a different result.
select sysdate from dual where sysdate between '1-Jan-10' and '31-Dec-10';
SYSDATE
---------
04-AUG-10
SQL> alter session set nls_date_format = 'YYYY-MM-DD';
Session altered.
SQL> select * from dual where sysdate between '1-Jan-10' and '31-Dec-10';
no rows selected
(Notice that in the second instance no error is returned. It just assumes Jan 10, 0001 and Dec. 10th, 0031.)
I think the best way is to just input the quarter start date and quarter end dates without even bothering with to_date. I think if you use
between '1-Jan-10' and '31-Dec-10'
for example, then you don't (in Oracle I believe) need to_date and it isn't much more difficult than typing in the quarter number
To calculate in Oracle the first day of a quarter and the last day of a quarter from the year and quarter:
I Use the fact
start_month= -2 + 3 * quarter
last_month = 3 * quarter
variable v_year number
variable v_quarter number
exec :v_year :=2017
exec :v_quarter:=4
select :v_year as year,
:v_quarter as quarter,
to_date(:v_year||to_char(-2+3*:v_quarter,'fm00'),'yyyymm') as quarter_start,
last_day(to_date(:v_year||to_char(3*:v_quarter,'fm00')||'01 23:59:59','yyyymmdd hh24:mi:ss')) as quarter_end
from dual a;
YEAR|QUARTER|QUARTER_START |QUARTER_END
2017| 4|2017-10-01 00:00:00|2017-12-31 23:59:59