The code uses either a negative or positive integer of minutes
I have been using the below code successfully for a while, but then realised that if the input number of minutes is less than -60 (ie -15) the hh:mm value returned is positive and not negative. I can understand why this doesnt work, but am struggling to come up with an alternative.
Normally the values entered are above 60 or -60 which always ensures the hour value is the correct sign.
Select nvl(-15,0) mins, decode(to_char(trunc(-15/60),'9999') || ':' ||ltrim(to_char(mod(abs(-15),60), '00')),':','00:00', to_char(trunc(-15/60),'9999') || ':' ||ltrim(to_char(mod(abs(-15),60), '00'))) hrs_mins from dual
I am expecting/wanting to see -15 and -00:15
I guess you need another check - whether value you passed to this piece of code is positive or negative (see line #7). Also, I modified format model from 9999 to 9900.
SQL> SELECT NVL (&&value, 0) mins,
2 DECODE (
3 TO_CHAR (TRUNC (&&value / 60), '9900')
4 || ':'
5 || LTRIM (TO_CHAR (MOD (ABS (&&value), 60), '00')),
6 ':', '00:00',
7 CASE WHEN &&value < 0 THEN '-' END
8 || TO_CHAR (TRUNC (&&value / 60), 'FM9900')
9 || ':'
10 || LTRIM (TO_CHAR (MOD (ABS (&&value), 60), '00'))) hrs_mins
11 FROM DUAL;
Enter value for value: -15
MINS HRS_MINS
---------- ----------
-15 -00:15
SQL> undefine value
SQL> /
Enter value for value: 25
MINS HRS_MINS
---------- ----------
25 00:25
SQL>
Much simpler:
with
test_data (mins) as (
select 10 from dual union all
select 60 from dual union all
select 150 from dual union all
select 0 from dual union all
select null from dual union all
select -15 from dual union all
select -120 from dual union all
select -150 from dual union all
select -1587 from dual
)
select mins,
to_char(sign(mins) * (trunc(abs(mins) / 60) + mod(abs(mins), 60) / 100)
, 'fm99999990d00', 'nls_numeric_characters = :,') as hours_mins
from test_data
;
MINS HOURS_MINS
---------- ------------
10 0:10
60 1:00
150 2:30
0 0:00
-15 -0:15
-120 -2:00
-150 -2:30
-1587 -26:27
In your attempt you convert null to 0; if you have a good reason for that, you can do it here too. I didn't; in most cases null doesn't (or shouldn't) mean "zero".
Related
Using PL SQL, I need to format number to VARCHAR2. I need to show decimal places if they are not zero. And decimal separator should be comma.
INPUT
OUTPUT
0.2
'0,2'
100.4
'100,4'
22
'22'
Would something like this do?
SQL> with test (input) as
2 (select 0.2 from dual union all
3 select 100.4 from dual union all
4 select 22 from dual
5 )
6 select input,
7 case when input = trunc(input) then to_char(input, '999G990')
8 else to_char(input, '999G990D0')
9 end output
10 from test;
INPUT OUTPUT
---------- ----------
,2 0,2
100,4 100,4
22 22
SQL>
this is locale independent, does not limit or round numbers
with
test(input) as (
select 0.2 from dual union all
select 100.4 from dual union all
select -12.34567 from dual union all
select 1.23e34 from dual union all
select -1.23e-34 from dual union all
select 22 from dual
)
select input
,case when input < 0 then '-' end -- sign
||case when abs(input) < 1 then '0' end -- zero
||replace(to_char(abs(input)),to_char(0,'fmd'),',') -- comma
output
from test
/
I was curious to see how in Oracle 12c you can take a timestamp datatype and convert the records into EPOCH time to make them a number and then use that number to find any records within that date column that are within 1 minute of each other (assuming the same day if needed, or simply any calculations within 1 minute).
I tried the following but got an ORA-01873: the leading precision of the interval is too small error.
select (sold_date - to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS'))*86400 as epoch_sold_date from test1;
What is SOLD_DATE? For e.g. SYSDATE (function that returns DATE datatype), your code works OK.
SQL> select (sysdate
2 - to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
3 ) * 86400 as epoch_sold_date
4 from dual;
EPOCH_SOLD_DATE
---------------
1600807918
SQL>
As SOLD_DATE is a timestamp, but - it appears that fractions of a second aren't or special interest to you, cast it to DATE:
select (cast (systimestamp as date) --> this
- to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
) * 86400 as epoch_sold_date
from dual;
Saying that you get the same result for all rows: well, I don't, and you shouldn't either if SOLD_DATE differs.
SQL> with test (sold_date) as
2 (select timestamp '2020-09-22 00:00:00.000000' from dual union all
3 select timestamp '2015-03-18 00:00:00.000000' from dual
4 )
5 select sold_date,
6 (cast (sold_date as date)
7 - to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
8 ) * 86400 as epoch_sold_date
9 from test;
SOLD_DATE EPOCH_SOLD_DATE
------------------------------ ---------------
22.09.20 00:00:00,000000000 1600732800
18.03.15 00:00:00,000000000 1426636800
SQL>
One more edit: when you subtract two timestamps, result is interval day to second. If you extract minutes from it, you get what you wanted:
SQL> with test (sold_date) as
2 (select timestamp '2020-09-22 10:15:00.000000' from dual union all
3 select timestamp '2015-03-18 08:05:00.000000' from dual
4 )
5 select sold_date,
6 lead(sold_date) over (order by sold_date) next_sold_date,
7 --
8 lead(sold_date) over (order by sold_date) - sold_date diff,
9 --
10 extract(day from lead(sold_date) over (order by sold_date) - sold_date) diff_mins
11 from test
12 order by sold_date;
SOLD_DATE NEXT_SOLD_DATE DIFF DIFF_MINS
------------------------------ ------------------------------ ------------------------------ ----------
18.03.15 08:05:00,000000000 22.09.20 10:15:00,000000000 +000002015 02:10:00.000000000 2015
22.09.20 10:15:00,000000000
SQL>
In your case, you'd check whether extracted minutes value is larger than 1 (minute).
If you just want to see how many minutes are there between two timestamps, then
cast them to dates
subtract those dates (and you'll get number of days)
multiply it by 24 (as there are 24 hours in a day) and by 60 (as there are 60 minutes in an hour)
Something like this:
SQL> with test (date_1, date_2) as
2 (select timestamp '2020-09-22 10:15:00.000000',
3 timestamp '2020-09-22 08:05:00.000000' from dual
4 )
5 select (cast(date_1 as date) - cast(date_2 as date)) * 24 * 60 diff_minutes
6 from test;
DIFF_MINUTES
------------
130
SQL>
If you are just looking to compare dates and find rows that are within one minute of each other, you do not need to use epoch time. There are several solutions to this problem on this thread.
I have a number column, I need to replace the first number by 7 in oracle.
How to replace guys?
number want_number
4789654 7789654
2754678 7754678
1765689 7765689
For instance
REGEXP_REPLACE(number, '^\d', '7')
should work.
Or, a substring option with concatenation:
SQL> with test (num) as
2 (select 4789654 from dual union all
3 select 2754678 from dual union all
4 select 1765689 from dual
5 )
6 select num, '7' || substr(num, 2) wanted_num
7 from test;
NUM WANTED_NUM
---------- --------------------
4789654 7789654
2754678 7754678
1765689 7765689
SQL>
You could manipulate as numbers, rather than converting to (and presumably later back from) strings:
with your_table (original) as (
select 4789654 from dual
union all select 2754678 from dual
union all select 1765689 from dual
union all select 999 from dual
union all select 1000 from dual
union all select 1001 from dual
)
select original,
original
- trunc(original, -floor(log(10, original)))
+ 7 * power(10, floor(log(10, original))) as wanted
from your_table;
ORIGINAL WANTED
---------- ----------
4789654 7789654
2754678 7754678
1765689 7765689
999 799
1000 7000
1001 7001
The floor(log(10, original) gives you the magnitude of the number. As an example, for your first original value 4789654 that evaluates to 6. If you then do trunc(original, -floor(log(10, original))) that is trunc(4789654, -6), which zeros the six least significant digits, giving you 4000000. Subtracting that from the original value gives you 789654. Then power(10, floor(log(10, original))) gives you power(10, 6) which is 1000000, multiplying that by 7 gives you 7000000, and adding that back on gives you 7789654.
(This won't work if your original value is <= zero, but that looks unlikely?)
I have some tables in Oracle and I would like to know the variations of the table by sections of time.
I explain, I need a query/script to know how often data is loaded / updated in the table.
example
Can anyone give me ideas on how to do something like that or similar?
Thanks!
Split a day to hours (CTEs times and periods) and apply aggregates to values stored in your_table (which is joined to "fabricated" hours). For example:
SQL> with times as
2 (select trunc(sysdate) + (level - 1)/24 val
3 from dual
4 connect by level <= 25
5 ),
6 periods as
7 (select val val_from,
8 lead(val) over (order by val) val_to
9 from times
10 ),
11 your_table (date_column, ins, upd) as
12 (select trunc(sysdate) + 13/24 + 25/(24*60), 100, 18 from dual union all
13 select trunc(sysdate) + 13/24 + 25/(24*60), 225, null from dual union all
14 select trunc(sysdate) + 14/24 + 33/(24*60), 203, 112 from dual union all
15 select trunc(sysdate) + 15/24 + 15/(24*60), null, 687 from dual union all
16 select trunc(sysdate) + 15/24 + 18/(24*60), null, 987 from dual
17 )
18 select to_char(p.val_from, 'hh24:mi') ||' - '||
19 to_char(p.val_to , 'hh24:mi') period,
20 count(ins) cnt_insert,
21 count(upd) cnt_update
22 from periods p join your_table t on t.date_column >= p.val_from
23 and t.date_column < p.val_to
24 where p.val_to is not null
25 group by p.val_from, p.val_to
26 order by p.val_From;
PERIOD CNT_INSERT CNT_UPDATE
------------- ---------- ----------
13:00 - 14:00 2 1
14:00 - 15:00 1 1
15:00 - 16:00 0 2
SQL>
I would like to compare two time values. The first time value is a custom time which reprsents the start time, for example the column name is Business_Start_time and set to 6:00:00 am. I would also like to extract the time only from a column in Oracle which is a date field that looks like '5/1/2019 12:57:19 PM' and is called 'Completed_Date_Time'. The purpose of this is to compare the businses start date to the time a file was completed. I've tried to convert the 'Completed_Date_Time' field to 'HH24:MI:SS' format which seems to change the datatype to a char(8) value which does not allow me to compare two timestamps.
CAST(TO_CHAR(Completed_Date_Time, 'HH:MI:SS AM') AS CHAR(8))
Convert the values to TIMESTAMP and then you can subtract the values from the values truncated to the start of the day to get an INTERVAL containing the time since midnight and to get the difference you can subtract.
Oracle Setup:
CREATE TABLE table_name ( Business_Start_time, Completed_Date_Time ) AS
SELECT '6:00:00 AM',
TO_DATE( '5/1/2019 12:57:19 PM', 'DD/MM/YYYY HH12:MI:SS AM' )
FROM DUAL
Query:
SELECT ( completed_time - TRUNC( completed_time ) ) -
( start_time - TRUNC( start_time ) ) AS time_difference
FROM (
SELECT TO_TIMESTAMP( business_start_time, 'HH12:MI:SS AM' ) AS start_time,
CAST( Completed_Date_Time AS TIMESTAMP ) AS completed_time
FROM table_name
)
Output:
| TIME_DIFFERENCE |
| :---------------------------- |
| +000000000 06:57:19.000000000 |
db<>fiddle here
Although you wrote both the question and a comment, I'm still not sure what you have and what you want to get. Sample case would help (create table & insert into).
Meanwhile, a few words about it: when subtracting two DATE datatype values, the result is number of days, which means that - if you want to display it in a format which is easier to read & understand - you have to do some calculations (a day has 24 hours; an hour has 60 mintues; and so forth).
Here's an example:
SQL> create table test
2 (business_Start_time date,
3 completed_date_Time date
4 );
Table created.
SQL> insert into test (business_start_time, completed_date_time) values
2 (to_date('05.01.2019 12:57:19', 'dd.mm.yyyy hh24:mi:ss'),
3 to_date('05.01.2019 18:58:20', 'dd.mm.yyyy hh24:mi:ss'));
1 row created.
Simply subtracted, you'd get
SQL> select completed_date_time - business_start_time result from test;
RESULT
----------
,250706019
SQL>
Here's a function which presents such a value in another format, dd:hh:mi (days:hours:minutes) (you can omit days by setting the second parameter to 0):
SQL> create or replace
2 function f_days2ddhhmi (par_broj_dana in number, par_cb_dd in number)
3 return varchar2
4 is
5 /* Converting number of days into dd:hh:mi format
6
7 Date from Date to Diff (days) Retval
8 -------------------- -------------------- -------------- ----------------------------------
9 20.11.2018. 07:00:00 - 20.11.2018. 13:45:00 0,28125 0:06:45 (6 hours 45 minutes)
10 23.10.2018. 07:00:00 - 25.10.2018. 22:12:00 2,63333 2:15:12 (2 daysa 15 hours 12 minutes)
11
12 PAR_BROJ_DANA: 0.28125
13 PAR_CB_DD : display number of days or not? 1 - yes --> 0:06:45
14 0 - no --> 06:45
15 */
16 l_broj_dana number := round (par_broj_dana, 15); -- to avoid 1.99999999999999 days = 1 day 24 hours
17 retval varchar2 (20);
18 begin
19 with podaci
20 as (select trunc (l_broj_dana) broj_dana,
21 round (mod (l_broj_dana * 24, 24), 2) broj_sati
22 from dual)
23 select decode (par_cb_dd,
24 1, lpad (p.broj_dana, 2, '0') || ':',
25 0, null)
26 || lpad (trunc (p.broj_sati), 2, '0')
27 || ':'
28 || lpad (round ( (p.broj_sati - trunc (p.broj_sati)) * 60),
29 2,
30 '0')
31 into retval
32 from podaci p;
33
34 return retval;
35 end f_days2ddhhmi;
36 /
Function created.
Applied to the test table, you'd get
SQL> select f_days2ddhhmi(completed_date_time - business_start_time, 0) result
2 from test;
RESULT
--------------------------------------------------------------------------------
06:01
which means that the difference is 6 hours and 1 minute.
If that's what you asked, see whether you can use it. Feel free to enhance it to seconds etc. if necessary.