Skip to Main Content
  • Questions
  • How to get sysdate of remote DB over DBLink to cope whith clock difference?

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Christian.

Asked: October 20, 2005 - 10:32 am UTC

Last updated: October 21, 2005 - 11:09 am UTC

Version: 9.2.0

Viewed 10K+ times! This question is

You Asked

Hi Tom,

I hope this is really a "new" question, but searching for "sysdate over dblink" or "remote sysdate" or such I did not find anything.

How can I get the sysdate of a remote DB using a dblink?
"select sysdate from dual@remote_db" will get "my" sysdate.

My problem is:
I need to extract data from several db's into my central dwh.
Some of these dbs/servers are not using a time server, so clocks are out of sync. When I then order the data by time, I get all sorts of problems: it is "event data" for which chronologically correct order over all source dbs is very important.
All my ETL is in the db using PL/SQL scheduled as db jobs.

As a quick solution I manually connected to each remote db, compared the remote time to my time, noted the difference and used it as an "offset" to add/subtract from the date columns in my extraction routine.
Of course, this is an ugly thing, when the clock of the remote db is changed, I use the wrong "offset" so I have to regularly check this.
Also exactness is very limited... I do it by hand with two sqlplus-session...

I know that I could create a view in the remote dbs that selects sysdate there and I then access this view. But in my case I have to extract data from db's where I'm not allowed to create objects in - and not because of some silly police of my company which I might be able to change - it is a third party product using an oracle db sort of "embedded" - no way of doing anything there. And it's oracle version 7somewhat on the remote dbs'... ugly enough - I have to use a sort of proxy-db (9.0.1) to be able to access them from 9iR2 at all.

Do you have an idea?

Many thanks
Christian



and Tom said...

you can use a call to dbms_sql@remote_site to accomplish this.

Just call dbms_sql over the dblink and parse, bind, execute and retrieve the host variable from:

begin :x := sysdate; end;







Rating

  (4 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

I get it to work only locally but not over DB-Link: ORA-01001

Christian Veit, October 21, 2005 - 6:10 am UTC

Hi Tom,

many thanks for the fast answer!

I created a test-function which worked locally.

But I have a problem, I can't get my code to work over the DB-link:

create or replace function test_remote_sysdate
return date
as
l_cursor int;
l_status int;
l_remote_sysdate date;
begin
l_cursor := dbms_sql.open_cursor;

dbms_sql.parse@REMOTE_DB1
( l_cursor,
'begin :x := sysdate; end;',
dbms_sql.native
);

dbms_sql.bind_variable( l_cursor, ':x', l_remote_sysdate );

l_status := dbms_sql.execute( l_cursor );
dbms_sql.variable_value(l_cursor, ':x', l_remote_sysdate );

dbms_sql.close_cursor(l_cursor);

return l_remote_sysdate;
end;
/

select sysdate, test_remote_sysdate from dual

ORA-01001: invalid cursor
ORA-06512: at "SYS.DBMS_SYS_SQL", line 826
ORA-06512: at "SYS.DBMS_SQL", line 39
ORA-06512: at "XXX.TEST_REMOTE_SYSDATE", line 11

(The db-link itself works, it is just a "loopback" to my db.)

Do you have an idea? Do I need any grants on the remote side?

Tom Kyte
October 21, 2005 - 8:29 am UTC

you have to call dbms_sql at the remote site FOR ALL OF THEM.

dbms_sql.open_cursor - must open REMOTE cursor
bind - must bind REMOTE cursor...

Sorry my mistake, it works - I must use dbms_sql.xxx@REMOTE_DB everywhere

Christian Veit, October 21, 2005 - 7:01 am UTC

Hi Tom,

sorry, I certainly must call DBMS_SQL at the remote site everywhere (use dbms_sql.xxx@REMOTE_DB everywhere).

Now it works:

create or replace function test_remote_sysdate
return date
as
l_cursor int;
l_status int;
l_remote_sysdate date;
begin

l_cursor := dbms_sql.open_cursor@REMOTE_DB;

dbms_sql.parse@REMOTE_DB
( l_cursor,
'begin :x := sysdate; end;',
dbms_sql.native
);

dbms_sql.bind_variable@REMOTE_DB
( l_cursor, ':x', l_remote_sysdate );

l_status := dbms_sql.execute@REMOTE_DB
( l_cursor );

dbms_sql.variable_value@REMOTE_DB
(l_cursor, ':x', l_remote_sysdate );

dbms_sql.close_cursor@REMOTE_DB
(l_cursor);

return l_remote_sysdate;
end;
/

It is very cool to be able to just

select sysdate - test_remote_sysdate as TIME_OFFSET from dual;

and get the time difference!


On more question:
I have several remote DB's/several DB-links,
so I want to pass the DB-link as a parameter into my function.
I guess I then have to use DBMS_SQL embedded in DBMS_SQL - dynamic dynamic sql?
I'm just trying this and get mad with all the '''' ...
Also debugging is tough!!!

Do you have a tip?

Thanks again!!!

Tom Kyte
October 21, 2005 - 8:32 am UTC

Now that you have the code, you just need to take your block, double up the ticks and get your output, something like this:


execute immediate
replace(
'declare
l_cursor int;
l_status int;
l_remote_sysdate date;
begin

l_cursor := dbms_sql.open_cursor@REMOTE_DB;

dbms_sql.parse@REMOTE_DB
( l_cursor,
''begin :x := sysdate; end;'',
dbms_sql.native
);

dbms_sql.bind_variable@REMOTE_DB
( l_cursor, '':x'', l_remote_sysdate );

l_status := dbms_sql.execute@REMOTE_DB
( l_cursor );

dbms_sql.variable_value@REMOTE_DB
(l_cursor, '':x'', l_remote_sysdate );

dbms_sql.close_cursor@REMOTE_DB
(l_cursor);

:MY_OUTPUT := l_remote_sysdate;
end;',
'@REMOTE_DB',
l_db_link )

into l_my_output;

It's working now!

Christian Veit, October 21, 2005 - 10:39 am UTC

Hi Tom,

many thanks!
Now with your help I ended up with the following working solution. I had some trouble to figure out that I have to use 'using out', not 'into'.

Perhaps it is interesting for other people as well, clocks not in sync between different dbs/servers seem fairly common.

create or replace
function remote_sysdate_at
( p_db_link_name in varchar2 )
return date
as
l_remote_sysdate date;
begin

execute immediate
replace
('declare ' ||
' l_cursor int; ' ||
' l_status int; ' ||
' l_date date; ' ||
'begin ' ||

' l_cursor := dbms_sql.open_cursor@<REMOTE_DB>; ' ||

' dbms_sql.parse@<REMOTE_DB> ' ||
' ( l_cursor, ' ||
' ''begin :x := sysdate; end;'', ' ||
' dbms_sql.native ' ||
' ); ' ||

' dbms_sql.bind_variable@<REMOTE_DB> ' ||
' ( l_cursor, '':x'', l_date ); ' ||

' l_status := dbms_sql.execute@<REMOTE_DB> ' ||
' ( l_cursor ); ' ||

' dbms_sql.variable_value@<REMOTE_DB> ' ||
' (l_cursor, '':x'', l_date ); ' ||

' dbms_sql.close_cursor@<REMOTE_DB> ' ||
' (l_cursor); ' ||

' :Y := l_date; ' ||
'end; '
,'<REMOTE_DB>', p_db_link_name
)
using out l_remote_sysdate
;

return l_remote_sysdate;

end;
/

select sysdate, remote_sysdate_at('DBLINK_NAME_TO_REMOTE_DB') from dual;

Then I saw: the first time I call it on a remote db, the difference between my sysdate and the remote is greater than all the follwing times - it must be the time it takes to connect to the remote db, the following times the connection is already there and is reused.

Ok, I could cope with that: just call it two times and use the second result.
Of course I cannot do anything about the parse/execute/network time, but do you know of other things which could come into play there?
Should I call it say 20 times, ignore the first result and do an average?

By the way, it this not something also the DB has to cope with concerning distributed transactions and such?
How does the DB do it?


Tom Kyte
October 21, 2005 - 11:09 am UTC

the database doesn't use wall clocks, we use SCN's and when we talk to eachother, we raise our SCNS to be in sync with them. The SCN is the Oracle clock.

my way of getting remote sysdate

Berny, July 04, 2006 - 5:42 am UTC

Hello,

I just use this function on remote DBs to get sysdate from there:

create or replace function Now return date is
begin
return(sysdate);
end Now;

I can select the local and remote sysdate then as:

select sysdate, now@dblink from dual;

More to Explore

DBMS_SQL

More on PL/SQL routine DBMS_SQL here