Copying data from LOB Column to Long Raw Column - oracle

I was looking for a query which picks data from a table having Blob column and update a table having LONG RAW column. It seems Oracle supports only up to 4000 characters. Is there a way to copy full data from blob to long raw.
I was using the follwing query
insert into APPDBA.QA_SOFTWARE_DUMMY
select SOFTWARE_ID, UPDATED_BY, CREATE_CHANGE_DATE, FILE_NAME,
DBMS_LOB.SUBSTR(SOFTWARE_FILE, 4000) SOFTWARE_FILE, SOFTWARE_TYPE
from APPDBA.QA_SOFTWARE_DUMMY_TEST ;
but DBMS_LOB.SUBSTR supports only upto 4000 characters.
Any help is highly appreciated.

PL/SQL will only read/write the first 32k of a LONG RAW and SQL will convert the column as a RAW so will only deal with the first 2000 bytes.
You can use java to access LONG RAW columns directly from the DB, as demonstrated in the question "Get the LENGTH of a LONG RAW".
Here's a little example, first the setup:
SQL> CREATE TABLE t (ID NUMBER PRIMARY key, source BLOB, destination LONG RAW);
Table created
SQL> DECLARE
2 l_lob BLOB;
3 BEGIN
4 INSERT INTO t VALUES (1, 'FF', '') RETURNING SOURCE INTO l_lob;
5 FOR i IN 1..10 LOOP
6 dbms_lob.writeappend(l_lob, 4000,
7 utl_raw.overlay('FF', 'FF', 1, 4000, 'FF'));
8 END LOOP;
9 END;
10 /
PL/SQL procedure successfully completed
The java class:
SQL> CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "Raw" AS
2 import java.io.*;
3 import java.sql.*;
4 import oracle.jdbc.driver.*;
5
6 public class Raw {
7
8 public static void updateRaw(int pk) throws SQLException,IOException {
9
10 Connection conn = new OracleDriver().defaultConnection();
11
12 PreparedStatement ps = conn.prepareStatement
13 ( "SELECT dbms_lob.getlength(source) length, source "
14 + "FROM t WHERE id = ? FOR UPDATE");
15 ps.setInt( 1, pk);
16 ResultSet rs = ps.executeQuery();
17
18 rs.next();
19 int len = rs.getInt(1);
20 InputStream source = rs.getBinaryStream(2);
21 byte[] destArray = new byte[len];
22 int byteRead = source.read(destArray);
23 ps = conn.prepareStatement(
24 "UPDATE t SET destination = ? WHERE id = ?");
25 ((OraclePreparedStatement) ps).setRAW(1,
26 new oracle.sql.RAW(destArray));
27 ps.setInt(2, pk);
28 ps.execute();
29 }
30 }
31 /
Java created
You can call this procedure from PL/SQL:
SQL> CREATE OR REPLACE
2 PROCEDURE update_raw(p_id NUMBER)
3 AS LANGUAGE JAVA NAME 'Raw.updateRaw(int)';
4 /
Procedure created
SQL> exec update_raw(1);
PL/SQL procedure successfully completed

Despite the fact that you make a reversal (normaly you should move from LONG to LOB, LONG being obsolete)...
You must use dbms_lob package, and make some plsql:
Eventualy you can use read, getlength...
Doc you can find here Psoug.org
or on Oracle doc

Related

PL/SQL MERGE INTO statement ignored

I want to create procedure will move the product (isdiscontinued=1) to another table (Product_discontinued_[NTID]). Structure of the two tables is identical.
PROCEDURE move_table AS
begin
merge into PRODUCTS_DISCONTINUTED b
using PRODUCTS a
on (a.ID = b.ID)
when matched then
update set b.id = a.id,b.productname = a.productname,b.supplierid=a.supplierid,b.unitprice=a.unitprice,
b.package=a.package, b.isdiscontinuted=a.isdiscontinuted
where a.isdiscontinuted = 1
when not matched then
insert (id,productname,supplierid,unitprice,package,isdiscontinuted)
values( a.id,a.productname,a.supplierid,a.unitprice,a.package,a.isdiscontinuted)
where a.isdiscontinuted = 1; END move_table;
I tried to write a procedure to move table PRODUCTS_DISCONTINUTED to PRODUCTS with using MERGE INO statement but it always keep getting the following errors. Could you please help me. Much appreciated.
[enter image description here][2]
error
Terminate merge and remove null;
when not matched then
insert ...
where a.isdicontinuted = 1; --> semi-colon here
-- null; --> remove this
end move_table;
I created tables involved, simply by looking at code you posted. Doing so, procedure gets created:
SQL> CREATE OR REPLACE PROCEDURE move_table
2 AS
3 BEGIN
4 MERGE INTO PRODUCTS_DISCONTINUTED b
5 USING PRODUCTS a
6 ON (a.ID = b.ID)
7 WHEN MATCHED
8 THEN
9 UPDATE SET b.id = a.id,
10 b.productname = a.productname,
11 b.supplierid = a.supplierid,
12 b.unitprice = a.unitprice,
13 b.package = a.package,
14 b.isdiscontinuted = a.isdiscontinuted
15 WHERE a.isdiscontinuted = 1
16 WHEN NOT MATCHED
17 THEN
18 INSERT (id,
19 productname,
20 supplierid,
21 unitprice,
22 package,
23 isdiscontinuted)
24 VALUES (a.id,
25 a.productname,
26 a.supplierid,
27 a.unitprice,
28 a.package,
29 a.isdiscontinuted)
30 WHERE a.isdiscontinuted = 1;
31 END move_table;
32 /
Procedure created.
SQL>
However, error you specified:
ORA-00904: "ISDISCONTINUTED" invalid identifier
means that there's no column whose name is ISDISCONTINUTED. Are you sure you didn't make a typo? Compare table description to code in the procedure - I guess there's a mismatch.

How to return rows based on the database user and the table's contents?

I have a following table:
id name score
1 SYS 4
2 RHWTT 5
3 LEO 4
4 MOD3_ADMIN 5
5 VPD674 4
6 SCOTT 5
7 HR 4
8 OE 5
9 PM 4
10 IX 5
11 SH 4
12 BI 5
13 IXSNEAKY 4
14 DVF 5
I want to create a policy function in Oracle SQL that makes sure of the following things:
If a user(Leo) is executing a select statement on this table, it only gets 3 LEO 4.
sys_dba gets all the results no matter what.
I have given select permissions to Leo on this table created by Scott.
I am getting stuck at writing this complex PL/SQL function. I tried the following and it states compilation errors. Also, I think it does not do what I intend to do:
CREATE FUNCTION no_show_all (
p_schema IN NUMBER(5),
p_object IN VARCHAR2
)
RETURN
AS
BEGIN
RETURN 'select avg(score) from scott.rating';
END;
/
Based on your previous question and info you posted, here's how I understood the question: if you granted select on the whole table to any user, then it is able to fetch all rows from it. You have to further restrict values.
One option - as we're talking about the function - is to use case in where clause.
Here's an example.
Sample data:
SQL> create table rating as
2 select 1 id, 'sys' name, 4 score from dual union all
3 select 3, 'leo' , 3 from dual union all
4 select 6, 'scott' , 5 from dual union all
5 select 7, 'hr' , 2 from dual;
Table created.
Function:
it accepts username as a parameter (mind letter case! In my example, everything is lowercase. In your, perhaps you'll have to use upper function or something like that)
case says: if par_user is equal to sys, let it fetch all rows. Otherwise, fetch only rows whose name column's value is equal to par_user
return the result
So:
SQL> create or replace function f_rating (par_user in varchar2)
2 return number
3 is
4 retval number;
5 begin
6 select avg(score)
7 into retval
8 from rating
9 where name = case when par_user = 'sys' then name
10 else par_user
11 end;
12 return retval;
13 end;
14 /
Function created.
Let's try it:
SQL> select f_rating('sys') rating_sys,
2 f_rating('hr') rating_hr
3 from dual;
RATING_SYS RATING_HR
---------- ----------
3,5 2
SQL>
I suggest creating a view for each user, like so
create view THE_VIEW as select * from TABLE where NAME = user
Then grant access to the view only.
Now it doesn't matter what kind of query a user tries to perform on your table, she will only get one row back.
Of-course the DBA user can access all the table data.

Using existing Database Sequence and creating custom generator

I have created a sequence in my Database as follows:
CREATE SEQUENCE "SCOTT"."ATA_SEQ_USERID"
MINVALUE 1 MAXVALUE 9999999999999999999999999999
INCREMENT BY 1 START WITH 1000 CACHE 20 NOORDER NOCYCLE ;
Now, I want to append the first two letters of the name of the user to the number created by this sequence and generate a user id everytime a new user registers, using Hibernate. How can I do that?
You just need to use:
SUBSTR function
concatenation operator || .
For example,
SQL> CREATE SEQUENCE s;
Sequence created.
SQL>
SQL> SELECT substr(ename, 1, 2)||s.nextval custom_seq FROM emp;
CUSTOM_SEQ
------------------------------------------
SM1
AL2
WA3
JO4
MA5
BL6
CL7
SC8
KI9
TU10
AD11
JA12
FO13
MI14
14 rows selected.
SQL>
Finally found the way to do it.
String sql = "select MY_SEQ_ID.nextval from dual";
SQLQuery query = session.getCurrentSession().createSQLQuery(sql);
List idList=query.list();
BigDecimal number=(BigDecimal) idList.get(0);
System.out.println(number);
where MY_SEQ_ID is my sequence id.

oracle raw type iteration

I am working with a table that contains a raw(200) field. From my client application I manage to get the value and store it in a byte[] and so that I can loop over it and get all the samples.
My raw data would be like ...
2C2B2E2B2D2C2933283030332B2F2D302F2B272F312E2B2F2F28242A2F322E
... and from there I would like to go from hex to decimal values and get an array such as 44,43,46,43
However, I would like to do a similar thing in a procedure but I don't know how to iterate over a raw field or how to cast it to byte array.
I tried with UTL_RAW.CAST_TO_BINARY_INTEGER but that would only give me the first sample
Given this data ...
SQL> select col1
2 from t23
3 /
COL1
--------------------------------------------------------------------------------
32433242324532423244324332393333323833303330333332423246324433303246324232373246
33313245324232463246323832343241324633323245
SQL>
... a SELECT like this will produce the requisite output...
SQL> select regexp_substr(utl_raw.cast_to_varchar2(col1), '([A-Z0-9]{2})', 1, level)
2 from t23
3 connect by level <= ceil(utl_raw.length(col1)/2)
4 /
REGEXP_SUBSTR(UTL_RAW.CAST_TO_VARCHAR2(COL1),'([A-Z0-9]{2})',1,LEVEL)
--------------------------------------------------------------------------------
2C
2B
2E
2B
...
2B
2F
2F
28
24
2A
2F
32
2E
31 rows selected.
SQL>
Use TO_NUMBER with the 'XX' mask to convert the hex into decimal ...
SQL> select to_number(
2 regexp_substr(utl_raw.cast_to_varchar2(col1), '([A-Z0-9]{2})', 1, level)
3 , 'XX')
4 from t23
5 connect by level <= ceil(utl_raw.length(col1)/2)
6 /
TO_NUMBER(REGEXP_SUBSTR(UTL_RAW.CAST_TO_VARCHAR2(COL1),'([A-Z0-9]{2})',1,LEVEL),
--------------------------------------------------------------------------------
44
43
46
43
45
44
41
...
Finally, to populate an array, and populate it in PL/SQL with the bulk collection syntax:
create type int_nt as table of integer
/
declare
ints int_nt;
begin
select to_number(
regexp_substr(utl_raw.cast_to_varchar2(col1), '([A-Z0-9]{2})', 1, level)
, 'XX')
bulk collect into ints
from t23
connect by level <= ceil(utl_raw.length(col1)/2);
end;
/
Probably there's a more efficient way to solve this but I managed to get my result by using utl_raw.length and utl_raw.substr over my raw data and iterating with an standard plsql loop and converting each substring to decimal with utl_raw.cast_to_binary_integer

Getting the timestamp of a file using PL/SQL

I am trying to get the timestamp of a text file abc.txt in some directory XYZ in oracle server. This file can get updated at any time in the day and i have to check if the file was updated any time after yesterday midnight, if yes i need to email that file as an attachment.
Is there any other way i can check this?
I have searched a lot over internet but could not find the solution.
Seriously not getting a clue of how to get it done.
It would be great if anyone could guide me.
Thanks.
I think you will have to do this by writing a java procedure, as described here by Tom Kyte:
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:439619916584
GRANT JAVAUSERPRIV to <your user>
/
create global temporary table DIR_LIST
( filename varchar2(255) )
on commit delete rows
/
create or replace and compile java source named "DirList"
as
import java.io.*;
import java.sql.*;
public class DirList
{
public static void getList(String directory)
throws SQLException
{
File path = new File( directory );
String[] list = path.list();
String element;
for(int i = 0; i < list.length; i++)
{
element = list[i];
#sql { INSERT INTO DIR_LIST (FILENAME)
VALUES (:element) };
}
}
}
/
create or replace procedure get_dir_list( p_directory in varchar2 )
as language java
name 'DirList.getList( java.lang.String )';
/
Another approach might be to make use of the preprocessor directive for external tables.
Please have a look at Mr. Kyte's article in Nov/Dec 2012 Oracle Magazine. He is playing with unix df, you can play with unix ls or windows dir.
http://www.oracle.com/technetwork/issue-archive/2012/12-nov/o62asktom-1867739.html
SQL> create table df
2 (
3 fsname varchar2(100),
4 blocks number,
5 used number,
6 avail number,
7 capacity varchar2(10),
8 mount varchar2(100)
9 )
10 organization external
11 (
12 type oracle_loader
13 default directory exec_dir
14 access parameters
15 (
16 records delimited
17 by newline
18 preprocessor
19 exec_dir:'run_df.sh'
20 skip 1
21 fields terminated by
22 whitespace ldrtrim
23 )
24 location
25 (
26 exec_dir:'run_df.sh'
27 )
28 )
29 /
Table created.

Resources