This is not so unusual scenario: You have a table with one or more LOB fields on remote oracle server and you want to query that table across db-link. If you try simple SELECT statement you will be facing ORA-22992: cannot use LOB locators selected from remote tables
oooops, what next. There are several workarounds, let me try to tackle one easy solution.
This testing was done using oracle 10g running on remote windows machine and 11.2 running on local redhat5 server, but it should in general work for any 10g/11g servers. .for the second solution follow this link
.
.
SOLUTION 1
The first solution is actually very simple. Create a table (I prefer temporary table) on local oracle server, insert data from remote server to local server using INSERT INTO and then query local server. This can be all fully automated and hidden from users …
1. REMOTE SERVER
let’s first define one table on remote server and load it with some data, that’s all we need to do on remote server.
create table RemotetTableWithClob (myid number, myclob clob); insert into RemotetTableWithClob (myid, myclob) values (1, 'This is small test'); insert into RemotetTableWithClob (myid, myclob) values (2, 'Another Test'); insert into RemotetTableWithClob (myid, myclob) values (3, 'One more test'); commit;
.
2. LOCAL SERVER
first, let me create db-link (I have same userID on both servers, this will be passwordless db-link) and then try to run simple query
CREATE PUBLIC DATABASE LINK RemoteServer USING 'remoteserver';
select myid, substr(myclob, 1, 5) mytext from remotetablewithclob@RemoteServer;
ORA-22992: cannot use LOB locators selected from remote tables
as you can see I cannot query the CLOB field.
.
3. SIMPLE WORKAROUND
The simply workaround is to bring the data from remote server to local server and then query local table. I use temporary tables simply because I don’t want to worry about deleting the data and concurrent users.
create global temporary table LocalTempTableWithClob (myid number, myclob clob) ON COMMIT PRESERVE ROWS; insert into LocalTempTableWithClob Select * from RemoteTableWithClob@RemoteServer; select myid, substr(myclob, 1, 5) mytext from LocalTempTableWithClob; MYID MYTEXT -------------- 1 This 2 Anoth 3 One m
.
4. NOW THE REAL TRICK
As you can see (3) works pretty good and it can be the end of the post, but … what if you have multiple users who want to query the data. Are you going to ask them to run the insert every time the want to query the table?
Here is my solution to that problem. I basically use a function to insert data from remote to local table and then display the result of local table instead. This is all done in a single pass in a single function and can be hidden from users via view.
I use pipelined function because I really love them and hope more users are going to start using them.
CREATE TYPE object_row_type AS OBJECT ( MYID NUMBER, MYCLOB CLOB ); CREATE TYPE object_table_type AS TABLE OF object_row_type; CREATE OR REPLACE FUNCTION GETCLOB RETURN object_table_type PIPELINED IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN insert into LocalTempTableWithClob Select * from RemoteTableWithClob@RemoteServer; commit; FOR cur IN (SELECT myid, myclob from LocalTempTableWithClob) LOOP PIPE ROW(object_row_type(cur.myid, cur.myclob)); END LOOP; DELETE FROM LocalTempTableWithClob; COMMIT; RETURN; END; create view myRemoteData as SELECT * FROM TABLE(GetClob) a; select myid, substr(myclob, 1, 5) mytext from myRemoteData; MYID MYTEXT -------------- 1 This 2 Anoth 3 One m
As you can see the solution is very simple, the function is hidden behind the view and users do not even know about the data insert.
Please note this is a bit oversimplified scenario, you can easily extend the function to add custom WHERE clause (which I would highly recommend, you don’t want to move whole table over dblink every single time), you can even make the function very universal for any querying of LOB across db-link.
[...] Informatica ← Query CLOB or BLOB from oracle table across DB-LINK with a simple view – Solution&nb… [...]
Nice…..solution.
I just build a version of it here.
Did you every figure out how to pass myid = 1 to the GETCLOB function from the view
ie. select myid, substr(myclob, 1, 5) mytext from myRemoteData where myid = 1
Thanks
Vic
it should be really easy, simply say use the pipeline function with a parameter, I did not test the code below, but in a nutshell it should work like that.
CREATE OR REPLACE FUNCTION GETCLOB RETURN object_table_type (v_my_id number) PIPELINED IS
...
insert into LocalTempTableWithClob Select * from RemoteTableWithClob@RemoteServer where my_id = v_my_id;
...
END;
then call the SQL
SELECT * FROM TABLE(GetClob(1) ) a1. you should probably add more error checking code to the function
2. you can make it truly universal by passing whole SQL or whole WHERE clause as a varchar to the GETCLOB function and running insert into statement as dynamic SQL statement using execute immediate
Jiri, Thanks for the prompt reply
Yes, passing the parameter to the function is an important step.
The key is getting the sql statement that fired the function.
sys_context(‘USERENV’,'CURRENT_SQL’) only work for FGA and then only after select, not before.
.
Do you know a method of getting the sql statement that was the root of this chain of events.
Once I have the statement, I can parse out the where clause and add it to the insert statement that populates the temp table.
.
Thanks for your time and consideration
Vic
I am out of my computer and just thinking loud, you have few options here …
Are you looking to get this on a fly? SELECT * FROM TABLE(GetClob(1) ) a Can I ask more details why please.
I have clob table A on database DB1
I have clob table B on database DB2
I have a view on database DB1 defined as …
create view AB as
select * from A
union all
select * from TABLE(GETBLOB)
I would like the GETBLOB function to “bring over” only those records from DB2 that satisfy the WHERE clause … not the entire table.
ie: select * from AB where myid = 1
This works but the GETBLOB function inserts the entire table from DB2 into DB1 temp table. This is not practical with very large tables.
It would be ideal if the function could receive the WHERE clause (or entire SQL for parsing) and insert only those records.
Hope this helps clarify things.
Thanks
I am out oftown today, but I think there is an easy solution. Let me respond tonight when get back.
I did not test this, but theoretically it could work. Basically I first pull primary key from remote table only (all records but only primary key), then filter records I need and then get clob for those records (record by record)
let me know if this works, I would be really interested to see if it does
select table_primary_key
field_clob
from table1_local A
union all
select table_primary_key,
(select * from TABLE(GETBLOB(b.table_primary_key))
from table2_remote@remoteserver b
The following sql fails to run due to the “select *” in the sub-query … ORA-00913 too many values
select table_primary_key,
(select * from TABLE(GETBLOB(b.table_primary_key)))
from table2_remote@remoteserver b
Changed it to the following but now getting ORA-01427 single-row subquery returns more than one row
select table_primary_key,
(select table_blob_col from TABLE(GETBLOB(b.table_primary_key)))
from table2_remote@remoteserver b
Still cannot figure out how to pass the WHERE clause to the getblob funtion from within the view.
really? does this return more than one record?
(select table_blob_col from TABLE(GETBLOB(b.table_primary_key)))
I’m unfortunately out of town whole week, let’ me try to recreate it on my VMWARE when I get back
No, it only returns 1 row. Both tables on both bases have the same structure and the same pk. Again, thanks for all your help with this. It would be nice to get this sql working.
What happens when the first COMMIT finishes,
and before the process goes to FOR-loop, another transaction calls this Function?
Will the records of LocalTempTableWithClob table be doubled?
Will the first transaction get doubled records at (SELECT myid, myclob from LocalTempTableWithClob)?
Sorry for poor English…
Regards,
Try it
I am 3000 miles from my computer on loooong vacation and typing on iPhone….
It uses global temporary table and thus only the session which inserts the data can see the data. In another words if you run the code in 10 sessions at the same time, you will have no issues because each session will see only it’s own data.
Ops! the word, “GLOBAL TEMPORARY” slipped my mind. so sorry…
This entry helped me a lot and made me be much more interested in Oracle.
Thank you very much.
No problem, glad I could help
Hi Guys, Please also explain the scenario where we have to “insert row that are manipulated from same/another table.”
are you trying to read and later insert data into CLOB over db-link?
Jiri, Your example is excellent. Saved me lots of time. Thank you describing it so nicely…I literally cut and past the code and did minor changes it worked like gem.
wonderful.
What’s up to every one, it’s genuinely a pleasant for me to
visit this site, it contains precious Information.
I think this is one of the most significant info for me.
And i’m glad reading your article. But wanna remark on some general things, The web site style is ideal, the articles is really excellent : D. Good job, cheers