Meaning of Oracle's dump(systimestamp) bytes - oracle

I'm trying to understand what the bytes from the timestamp set on my DB mean. How do they get computed to generate the more readable date?
I'm using the below query to get the data that I need:
SELECT systimestamp
,DUMP (systimestamp)
,sessiontimezone
FROM dual;
And the output of my above query is:
+-------------------------------------+-----------------------------------------------------------------+------------------+
| systimestamp | dump(systimestamp) | sessiontimezone |
+-------------------------------------+-----------------------------------------------------------------+------------------+
| 31-JUL-15 08.55.06.157047000 +00:00 | Typ=188 Len=20: 223,7,7,31,8,55,6,0,216,88,92,9,0,0,5,0,0,0,0,0 | Europe/Bucharest |
+-------------------------------------+-----------------------------------------------------------------+------------------+
I have found a few resources online explaining what the bytes mean (here) but the rules don't match in my scenario.
For example: 223 is not the century + 100 etc.
The reason I'm trying to do this is because of a problem I'm facing when comparing the values in a timestamp(3) column with systimestamp and I'm trying to write a script to verify if my issue/solution is the same as explained here.
Any help is appreciated.

There a various superficially similar but internally different datetime datatypes. systimestamp is type 188 (and has timezone information); a timestamp literal is type 187 without time zone info and 188 with it; and a plain timestamp column is type 180:
select dump(systimestamp) from dual;
DUMP(SYSTIMESTAMP)
--------------------------------------------------------------------------------
Typ=188 Len=20: 223,7,7,31,9,50,28,11,128,203,79,35,1,0,5,0,0,0,0,0
select dump(timestamp '2015-07-31 08:55:06.157047 +00:00') from dual;
DUMP(TIMESTAMP'2015-07-3108:55:06.157047+00:00')
---------------------------------------------------------------
Typ=188 Len=20: 223,7,7,31,8,55,6,0,216,88,92,9,0,0,5,0,0,0,0,0
select dump(timestamp '2015-07-31 08:55:06.157047') from dual;
DUMP(TIMESTAMP'2015-07-3108:55:06.157047')
---------------------------------------------------------------
Typ=187 Len=20: 223,7,7,31,8,55,6,0,216,88,92,9,0,0,3,0,0,0,0,0
create table t (ts timestamp);
insert into t (ts) values (timestamp '2015-07-31 08:55:06.157047');
select dump(ts) from t;
DUMP(TS)
--------------------------------------------------------------------------------
Typ=180 Len=11: 120,115,7,31,9,56,7,9,92,88,216
Of those, only a timestamp column uses the internal format in the article you linked to, using excess-100 notation for the year.
For the others, the first byte is a base-256 modifier, and the second byte is the base 256 year; so you would interpret it as
223 + (7 * 256) = 2015
You can read more about the internal storage in My Oracle Support document 69028.1. That, and the earlier answer linked to in comments, refer to the two date types, but timestamps are treated the same down to the seconds, and some of the rest can be inferred for type 187/188 - the fractional-seconds part anyway:
Byte 1 - Base 256 year modifier: 223
2 - Base 256 year: 7 (256 * 7 = 1792 + 223 = 2015)
3 - Month: 7
4 - Day: 31
5 - Hours: 8
6 - Minutes: 55
7 - Seconds: 6
8 - Unused?
9 - Base 256 nanoseconds: 216
10 - Base 256 ns modifier 1: 256 * 88 = 22528
11 - Base 256 ns modifier 2: 256 * 256 * 92 = 6029312
12 - Base 256 ns modifier 3: 256 * 256 * 256 * 9 = 150994944
=> actual nanoseconds = 216 + 22528 + 6029312 + 150994944
=> 157047000
13-20 - Time zone data?
For type 120 the fractional seconds are the same but with the bytes reversed.

Related

Difference between 2 times in sec in Pl/SQL [duplicate]

This question already has answers here:
Calculate difference between 2 date / times in Oracle SQL
(21 answers)
Closed 8 months ago.
I need some help to identify difference between 2 times in sec
variable :
current_time : this is current time
check_begin_time : in YYYYMMDDHH24MMSS format
I need to check the difference i.e. current time - check_begin_time in secs
Thanks.
As difference of two DATE datatype values in Oracle represents number of days between them, you have to multiply it by 24 (as there are 24 hours in a day) and 60 (as there are 60 minutes in an hour) and 60 (as there are 60 seconds in a minute).
So:
(setting date format, just to know what is what; you don't have to do that)
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
I presume your value is stored as a string (according to date format you posted; though, it is wrong - minutes are MI, not MM) so first convert it to a DATE datatype value, and then do the calculation:
SQL> select sysdate now,
2 (sysdate - to_date('20220614090500', 'yyyymmddhh24miss')) * 24 * 60 * 60 seconds
3 from dual;
NOW SECONDS
------------------- ----------
14.06.2022 09:09:54 294
SQL>
SELECT (END_DT - START_DT) * 60 * 60 * 24 FROM MY_TABLE; -- Seconds
SELECT (END_DT - START_DT) * 60 * 24 FROM MY_TABLE; -- Minutes
SELECT (END_DT - START_DT) * 24 FROM MY_TABLE; -- Hours
To get more info refer to this link: Date Difference

How would I add an artificial termination date to the termination date column based on two different dates for the same patient id

I need to figure out a query that will compare two EFFECTIVE dates for a given patient number with different HMOs and determine which is the later date of the two and then populate a TERMINATION date field for only the older of the two effective dates with the last day of the previous month of the newer effective date of the two. This needs to be done across multiple patient, HMO, effective date combinations in a table.
SELECT * FROM tablename
The output is this:
HMO PATIENT EFFECTIVE TERMINATION
16 221135 01-APR-18
18 221135 01-OCT-17
12 251181 01-SEP-16
16 251181 01-MAR-15
12 271126 01-MAR-15
16 271126 01-DEC-16
12 291141 01-DEC-16
16 291141 01-FEB-19
12 391134 09-MAY-13
16 391134 01-APR-18
What I am trying to do via a query or queries is this:
HMO PATIENT EFFECTIVE TERMINATION
16 221235 01-APR-18
18 221235 01-OCT-17 3/31/2018
12 251381 01-SEP-16
16 251381 01-MAR-15 8/31/2016
12 2711126 01-MAR-15 11/30/2016
16 2711126 01-DEC-16
12 292241 01-DEC-16 1/31/2019
16 292241 01-FEB-19
12 391534 09-MAY-13 31-MAR-19
16 391534 01-APR-18
I've tried using a case statement but it is unsurprisingly creating four rows per patient, hmo combo and populating two of the rows with dates and leaving two blank:
SELECT DISTINCT
S.HMO
,S.PATIENT
,S.EFFECTIVE
,CASE WHEN S.EFFECTIVE > E.EFFECTIVE THEN LAST_DAY(ADD_MONTHS(S.EFFECTIVE, -1))
WHEN S.EFFECTIVE < E.EFFECTIVE THEN LAST_DAY(ADD_MONTHS(E.EFFECTIVE, -1))
ELSE NULL END AS TERMINATION
FROM tablename S INNER JOIN tablename E ON S.PATIENT=E.PATIENT
WHERE S.PATIENT =221135
Any ideas or advice would be welcome.
With sample data you posted:
SQL> select * from tablename order by patient, effective;
HMO PATIENT EFFECTIVE TERMINATIO
---------- ---------- ---------- ----------
18 221135 10/01/2017
16 221135 04/01/2018
16 251181 03/01/2015
12 251181 09/01/2016
12 271126 03/01/2015
16 271126 12/01/2016
6 rows selected.
such a MERGE might do:
SQL> merge into tablename a
2 using (select patient, max(effective) max_effective,
3 min(effective) min_effective
4 from tablename
5 group by patient
6 ) x
7 on (a.patient = x.patient)
8 when matched then update set
9 a.termination = x.max_effective - 1
10 where a.effective = x.min_effective;
3 rows merged.
Result is then
SQL> select * from tablename order by patient, effective;
HMO PATIENT EFFECTIVE TERMINATIO
---------- ---------- ---------- ----------
18 221135 10/01/2017 03/31/2018
16 221135 04/01/2018
16 251181 03/01/2015 08/31/2016
12 251181 09/01/2016
12 271126 03/01/2015 11/30/2016
16 271126 12/01/2016
6 rows selected.
SQL>

Need specific number format in Oracle S9(15)V9(2)

I have a requirement to produce amount fields in zoned decimal format with this specific syntax below.
I don’t know if I need to create a function to handle this or if I can tweak the Oracle number format model. I’m thinking it might require some conditional formatting within a function due to the different requirement for number of digits between positive and negative. I will be performing this formatting on a couple of dozen data elements in the procedure so that might be another reason to use a function. Thoughts?
Requirement:
Amount should be represented by 17 characters (positive number) or 16 characters plus a “}” appended to the end (negative number).
Ex. 0.00 should show as 00000000000000000.
Ex. -935,560.00 should show as 00000000093556000}
Using Oracle 12c.
If I understood you correctly, the input is already formatted and its datatype is VARCHAR2. If that's so, then this might do the job:
SQL> with test (col) as
2 (select '0.00' from dual union all
3 select '25.34' from dual union all
4 select '-935,560.00' from dual
5 )
6 select col,
7 lpad(translate(col, 'x,.-', 'x'),
8 case when substr(col, 1, 1) = '-' then 16
9 else 17
10 end, '0') ||
11 case when substr(col, 1, 1) = '-' then '}'
12 else null
13 end result
14 from test;
COL RESULT
----------- --------------------
0.00 00000000000000000
25.34 00000000000002534
-935,560.00 0000000093556000}
SQL>
What does it do?
lines #1 - 5 - sample data
line #7 - translate removes minus sign, commas and dots
lines #7 - 10 - lpad pads the number (without characters from the previous step) with zeros up to the length of 16 (for negative values) or 17 (for positive values) characters
lines #11 - 13 - if it is a negative value, concatenate } to the end of the result string

Cannot filter null Datetime values

I have a problem that is driving me crazy. I have to query an oracle view that returns some DATETIME values.
The incredible problem is that even if I set the "IS NOT NULL" on the WHERE clause and even if I set the NVL(FECHA_HASTA, FECHA_DESDE), I´m still getting null values!!. How is that possible???
This is the query:
SELECT CUIL as Cuil,
COD_TIPO_CAUSAL as CodTipoCausal,
COD_CONVENIO as CodConvenio,
FECHA_DESDE as FechaDesde,
NVL(FECHA_HASTA, FECHA_DESDE) as FechaHasta
FROM ORGANISMO.VCAUSAL_AUSENCIA
WHERE FECHA_HASTA IS NOT NULL
AND FECHA_HASTA > (SELECT SYSDATE - 180 FROM SYS.DUAL)
AND CUIL IN (SELECT CUIL FROM ORGANISMO.VEMPLEADO WHERE FECHA_EGRESO IS NULL OR FECHA_EGRESO > (SELECT SYSDATE FROM SYS.DUAL))
EDIT:
Here is dump(fecha_hasta, 1016) added:
The dumped values show that the data is corrupt. The internal date format is well-known:
byte 1 - century (excess 100)
byte 2 - year (excess 100)
byte 3 - month
byte 4 - day
byte 5 - hour (excess 1)
byte 6 - minute (excess 1)
byte 7 - seconds (excess 1)
so the fourth byte in the two values that SQL Developer is reporting as null (even though they clearly are not actually null) should not be zero, as there is no day zero.
Based on those rules, 79,9d,2,0,18,3c,3c in hex, which is 121,157,2,0,24,60,60 in decimal, should convert as:
century: 121 - 100 = 21
year: 157 - 100 - 57
month: 2
day: 0
hour: 24 - 1 = 23
minute: 60 - 1 = 59
second: 60 - 1 = 59
or 2157-02-00 23:59:59. Similarly 78,b8,1,0,18,3c,3c converts to 2084-01-00 23:59:59.
Version 18.3 of SQL Developer displays those values, in both the script output and query results windows, as the previous day:
DT DUMPED
------------------- -----------------------------------
01-07-2020 23:59:59 Typ=12 Len=7: 78,78,7,1,18,3c,3c
31-01-2157 23:59:59 Typ=12 Len=7: 79,9d,2,0,18,3c,3c
31-12-2083 23:59:59 Typ=12 Len=7: 78,b8,1,0,18,3c,3c
01-07-2018 00:00:00 Typ=12 Len=7: 78,76,7,1,1,1,1
whereas db<>fiddle shows the zero-day values.
So, since they are not actually null, it's reasonable that is not null and nvl() didn't affect them, and then it's up to the client or application as to how to present them.
The real issue is that you seem to have corrupted data in the tables underlying the view you're querying, so that needs to be investigated and fixed - assuming the invalid values can be safely identified, and you can find out what they should have been in the first place, which might be a struggle. Just filtering them out, either as part of the view or in your query, won't be simple though - unless you can filter out dates in the future. And assuming all the corruption is both that obvious and pushing dates into the future; on some level you have to question the validity of all of those dates... there could be much more subtle corruptions that look OK.
And then whatever process or tool caused the corruption needs to be tracked down and fixed so it doesn't happen again. Lots of things can cause corruption of course, but I believe imp used to have a bug that could corrupt dates and numbers, and OCI programs can too.

Displaying the contents according to their heading type

I am new to SQL. My doubt is how to display the output inside the procedure like this.
I have various databases and its type and the current size all in one table 'predict_storage'
I want to display the output as:
UAT:
TMAP: 100G
TCIG: 200G
QA:
QMAP: 100G
QCIG: 200G
DR:
DRMAP: 100G
DRCIG: 200G
Where UAT is the database type and TMAP, TCIG are databases and the current size is 100g. So I want the output to like this categorized based upon the database type.
Here is the code:
CREATE OR REPLACE PROCEDURE test11 IS
db_original_name_var varchar2(30);
db_type varchar2(20);
db_name varchar2(40);
Used_space_var NUMBER;
MAX_Used_space_monthvar NUMBER;
RATE NUMBER;
avg_space_future_min NUMBER;
avg_space_future_max NUMBER;
avgsp NUMBER;
avg_space_3m NUMBER;
avg_space_6m Number;
avg_space_yr NUMBER;
CURSOR db_list_cur is
select original_db_name,database_type
from database_list;
Begin
open db_list_cur;
LOOP
fetch db_list_cur
into db_original_name_var,db_type;
EXIT WHEN db_list_cur%NOTFOUND;
select substr(avg(ave_used_space),0,6) INTO Used_space_var from month_space where db_original_name_var=database_name;
select substr(max(ave_used_space),0,6) INTO MAX_Used_space_monthvar from month_space where db_original_name_var=database_name;
--rate calc
RATE := (MAX_Used_space_monthvar-Used_space_var)/Used_space_var;
avg_space_future_max:=(Used_space_var)+(Used_space_var* RATE);
avg_space_future_min:=(Used_space_var)-(Used_space_var* RATE);
avgsp := (avg_space_future_max + avg_space_future_min)/2;
avg_space_3m :=(avgsp)+(avgsp* rate* 3);
avg_space_6m :=(avgsp)+(avgsp* rate* 6);
avg_space_yr :=(avgsp)+(avgsp* rate* 12);
insert into Predict_report
(
database_name ,
Grwoth_rate ,
Current_AVERAGE,
Space_3mn,
SPACE_6mn,
Space_yr,
database_type
)
values
(
db_original_name_var,
RATE,
round(avgsp,3),
round(avg_space_3m,3),
round(avg_space_6m,3),
round(avg_space_yr,3),
db_type
);
commit;
loop
dbms_output.put_line(db_type||':');
select database_name into db_name from predict_report where database_type=db_type;
dbms_output.put_line(db_name);
END LOOP;
END LOOP;
close db_list_cur;
END;
/
The tables I have are
1) Storage Info
DB_ID NUMBER(38)
DATABASE_NAME VARCHAR2(50)
DATABASE_SIZE NUMBER
TIME_STAMP DATE
FREE_SPACE VARCHAR2(50)
DATA_LINK_NAME VARCHAR2(50)
CUSTOMER_SPACE NUMBER
DATABASE_TYPE VARCHAR2(30)
USED_SPACE NUMBER
2) Database_list
DB_ID NUMBER
DB_NAME VARCHAR2(50)
DATALINK_NAME VARCHAR2(50)
DATABASE_TYPE VARCHAR2(20)
ORIGINAL_DB_NAME VARCHAR2(30)
3)Predict Report
DATABASE_NAME NOT NULL VARCHAR2(50)
GRWOTH_RATE NUMBER
CURRENT_AVERAGE NUMBER
SPACE_3MN NUMBER
SPACE_6MN NUMBER
SPACE_YR NUMBER
DATABASE_TYPE VARCHAR2(20)
4) Month_space
DATABASE_NAME NOT NULL VARCHAR2(50)
GRWOTH_RATE NUMBER
CURRENT_AVERAGE NUMBER
SPACE_3MN NUMBER
SPACE_6MN NUMBER
SPACE_YR NUMBER
DATABASE_TYPE VARCHAR2(20)
-----The sql file is
set echo off numf 999G999G999G999 lin 32000 trims on pages 50000 head on feed off markup html off
alter session set nls_numeric_characters='.''' nls_date_format='Day DD. Month, YYYY';
spool /tmp/report.html
--prompt To: jialin.zhu#synchronoss.com
prompt TO: varun.jain#synchronoss.com
prompt cc: varun.jain#synchronoss.com
prompt From: varun.jain#synchronoss.com
prompt Subject: Daily space report
prompt Content-type: text/html
prompt MIME-Version: 1.0
set markup html on entmap off table 'BORDER="2" BGCOLOR="white" FONTCOLOR="black"'
prompt <i>Good morning, </i>
prompt <i>Here is the Space report as on &_DATE</i>
prompt <i>Kind Regards, </i>
------------------------------------------------------------------------------------------------------------------------------------------------
prompt <br/><h3>Database Space Report</h3>
set serveroutput on
CLEAR COLUMNS
Set HEADING ON
Set COLSEP ,
SET PAGESIZE 20000
SET timing off feedback off verify off echo off
prompt <br/><h3> Environment Space Summary</h3>
column database_type heading 'Database Type'
column Sum(current_average) format 9999 HEADING 'Total Space in GB'
select database_type,Sum(current_average) from predict_report group by database_type;
--------------------------------------------------------------------------------------------------------------------------------------------------
prompt <br/><h3> DR database Summary</h3>
COLUMN ('THETOTALSPACEINDRDATABASES:'||SUM(CURRENT_AVERAGE)||''||'GB') format 9999 heading 'total Space in DR database'
select ('The total space in DR databases :' ||Sum(current_average)||' '||'GB') from predict_report where DATABASE_TYPE not in ('UAT','QA');
------------------------------------------------------------------------------------------------------------------------------------------
prompt <br/><h3> databases Summary</h3>
column database_name format a30 heading ' DATABASE NAME'
column round(GRWOTH_RATE*100,0)||'%' heading 'GROWTH RATE'
COLUMN CURRENT_AVERAGE FORMAT 9999 HEADING 'TODAYS SPACE in GB'
COLUMN SPACE_3MN FORMAT 9999 HEADING 'SPACE AFT 3 MONTHS in GB'
COLUMN SPACE_6MN FORMAT 9999 HEADING 'SPACE AFT 6 MONTHS in GB'
COLUMN SPACE_YR FORMAT 9999 HEADING 'SPACE AFT A YEAR in GB'
truncate table Predict_report;
set serveroutput on
exec report;
SELECT database_name,round(GRWOTH_RATE*100,0)||'%',current_average,space_3mn,space_6mn,space_yr FROM Predict_report;
---------------------------------------------------------------------------------
prompt <br/><h3>Database Space Summary</h3>
column sum(current_average) FORMAT 9999 heading 'Total Space in GB'
column sum(space_3mn) FORMAT 9999 heading 'Total Space in 3 months in GB'
column sum(space_6mn) FORMAT 9999 heading 'Total Space in 6 months in GB'
column sum(space_yr) FORMAT 9999 heading 'Total Space in 1 year in GB'
select sum(current_average),sum(space_3mn),sum(space_6mn),sum(space_yr) from Predict_report ;
spool off;
---------to send the mail----------------
host /usr/sbin/sendmail -t </tmp/report.html
and the output in the email received is like (It is in table form in the EMAIL)
DATABASE NAME GROWTH RATE TODAYS SPACE in GB SPACE AFT 3 MONTHS in GB SPACE AFT 6 MONTHS in GB SPACE AFT A YEAR in GB
CLNK 0% 199 200 200 202
CCIG 0% 562 563 563 565
DTXN 5% 330 377 424 518
DCIG 0% 414 416 418 422
QMAP 0% 16 16 17 17
QLNP 0% 44 44 44 44
QHTS 1% 32 32 33 34
QFKP 1% 37 38 39 41
QSAG 0% 168 169 170 172
CSAG 0% 812 815 818 824
LTATG 0% 25 25 25 25
QCIG 0% 208 209 209 211
TLNP 0% 341 341 341 341
TMAP 0% 60 61 61 62
TSAG 0% 223 226 228
The extra loop you have now for output never terminates, so executing the procedure will appear to hang, at least until you run out of dbms_output buffer space; and doesn't retrieve the current value. And it's in the wrong place, as it will show data for all databases of that type each time round the loop, so you'll get duplicates (at best).
Although it doesn't seem to be a sensible way to do it, not least because you're assuming the procedure will always be run by something that can show the output and has the output enabled, you could do something like this, after close db_list_cur:
db_type := null;
for r in (select database_type, database_name, current_average
from predict_report
order by database_type, database_name)
loop
if db_type is null or db_type != r.database_type then
dbms_output.put_line(r.database_type || ':');
db_type := r.database_type;
end if;
dbms_output.put_line(r.database_name ||' '||
to_char(nvl(r.current_average, 0), '999990.00') ||'G');
end loop;
... or within the main loop based on the data you're inserting, rather than querying the table at the end (since they should match); but this will work whether you use your loops or a much simpler insert into predict_report select ....
You don't need to use cursors to calculate the statistics or populate the table; you don't need PL/SQL at all really. You can just join the two tables together and work out the numbers from the returned values. Something like this would do it:
select database_name,
rate as grwoth_rate,
avg_used_space as current_average,
avg_used_space + (avg_used_space * rate * 3) as space3mn,
avg_used_space + (avg_used_space * rate * 6) as space6mn,
avg_used_space + (avg_used_space * rate * 12) as spaceyr,
database_type
from (
select database_name,
database_type,
avg_used_space,
max_used_space,
(max_used_space - avg_used_space)/avg_used_space as rate
from (
select dl.original_db_name as database_name,
dl.database_type,
trunc(avg(ave_used_space), 2) as avg_used_space,
trunc(max(ave_used_space), 2) as max_used_space
from database_list dl
join month_space ms on ms.database_name = dl.original_db_name
group by dl.original_db_name,
dl.database_type
)
);
You can use that to insert into the new table, though not sure you even need that really, if you're only producing output from within the procedure - not clear if that's the case, though you don't seem to want to show all the statistics you're calculating.
I'm also not sure your statistics make much sense though. You seem to be calculating a growth rate over an arbitrary number of months, and then predicting future sizes by applying that rate to the average over the life of the database rather than it's current size, which you don't seem to be collecting - you have the current average, which isn't the same thing.

Resources