How to insert BLOB into Oracle table via SQL - sql

I wanted to inser below as a BLOB into Oracle table
I looked at this solution as well.Sloution: But problem is pasting these chars in the SQL show ��
INSERT INTO JOB_DETAILS (JOB_DATA) VALUES (hextoraw('��'))

It's not the simple, because those are characters not the hex representation of the characters. For example
SQL> host cat data.txt
Hello
SQL> host od -x data.txt
0000000 6548 6c6c 206f 0a0d
0000010
So the hex for Hello appears to be "6548 6c6c 206f 0a0d"
But on this platform (windows) we byte swap data, and have a couple characters to terminate in the end of line. So when I ditch the last two (0a0d) and load it:
SQL> create table t ( b blob);
Table created.
SQL> insert into t values ( hextoraw('65486c6c206f'));
1 row created.
SQL> select utl_raw.cast_to_varchar2(b) from t;
UTL_RAW.CAST_TO_VARCHAR2(B)
-------------------------------------------------------------
eHll o
You can see that the bytes have been swapped. I need to take that into acount
SQL> delete t;
1 row deleted.
SQL> insert into t values ( hextoraw('48656c6c6f20'));
1 row created.
SQL> select utl_raw.cast_to_varchar2(b) from t;
UTL_RAW.CAST_TO_VARCHAR2(B)
-------------------------------------------------------------
Hello
But in reality, you'd rarely add a blob by using hex strings. Presumably your data is stored in a file. In which case, you would load it using DBMS_LOB.LOADBLOBFROMFILE, eg
SQL> set timing on
SQL> declare
2 l_bfile bfile;
3 l_blob blob;
4
5 l_dest_offset integer := 1;
6 l_src_offset integer := 1;
7 begin
8 insert into test_blob_bas (id, blob_data)
9 values (1, empty_blob())
10 return blob_data into l_blob;
11
12 l_bfile := bfilename('TEMP', 'myfile.dat');
13 dbms_lob.fileopen(l_bfile, dbms_lob.file_readonly);
14 dbms_lob.loadblobfromfile (
15 dest_lob => l_blob,
16 src_bfile => l_bfile,
17 amount => dbms_lob.lobmaxsize,
18 dest_offset => l_dest_offset,
19 src_offset => l_src_offset);
20 dbms_lob.fileclose(l_bfile);
21
22 commit;
23 end;
24 /
PL/SQL procedure successfully completed.

Related

IF condition containing IN operator inside ORACLE Trigger is not working

I have a trigger that uses IF condition with IN operator and two variables v_audit_user and v_evdnse_user inside IN. Both variables are containing comma separated ID values. The trigger gets compiled successfully with no errors. I am not understanding why the IF condition with IN is not working. When I select the function that assigns value to the variables independently, I do see the comma separated values, so nothing is wrong with function (see screenshot).
create or replace TRIGGER TRG_CHK_HRCQA_CASE_ACTIONS
AFTER INSERT ON KDD_CASE_ACTIONS
FOR EACH ROW
DECLARE
user_audit kdd_review_owner.OWNER_ID%TYPE; /* The user that is displayed in audit */
user_evdnse kdd_review_owner.OWNER_ID%TYPE; /* The user that took action in evidence tab */
v_audit_user NUMBER; /* The HRCO/QA user from audit tab */
v_evdnse_user NUMBER; /* The HRCO/QA user from evidence tab */
LV_ERRORCODE VARCHAR2(1000);
BEGIN
/* pass the username into the variables */
SELECT kro.OWNER_ID into user_audit from kdd_review_owner kro where kro.OWNER_SEQ_ID = :NEW.ACTION_BY_ID;
SELECT kro.OWNER_ID into user_evdnse from kdd_review_owner kro where kro.OWNER_SEQ_ID = :NEW.ACTION_BY_ID;
/* fetch the comma separated IDs */
v_audit_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID,user_audit,'AUDIT');
v_evdnse_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID,user_evdnse,'EVDNSE');
-- select ENTITY_ID into v_evdnse_user from table(f_get_arg_table(F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID,user_evdnse,'EVDNSE')));
/* If the action taken is by QA or HRCO role */
IF (:NEW.ACTION_SEQ_ID in (v_audit_user,v_evdnse_user))
THEN
/* then insert record in the SC_HRCQA_CASE_ACTIONS table with IS_HRCO_QA flag as Y */
Insert into SC_HRCQA_CASE_ACTIONS (ACTION_SEQ_ID,ACTION_BY_ID,ACTION_TS,STATUS_CD,CASE_INTRL_ID,ACTION_ID,NEW_CASE_OWNR_ASSGN_ID,CASE_DUE_TS,PREV_CASE_OWNR_ASSGN_ID,IS_HRCO_QA)
values (:NEW.ACTION_SEQ_ID, :NEW.ACTION_BY_ID, :NEW.ACTION_TS, :NEW.STATUS_CD, :NEW.CASE_INTRL_ID, :NEW.ACTION_ID, :NEW.NEW_CASE_OWNR_ASSGN_ID, :NEW.CASE_DUE_TS, :NEW.PREV_CASE_OWNR_ASSGN_ID,'Y');
-- ELSE
--
-- /* else the logged in user is NOT HRCO/QA hence insert record in the SC_HRCQA_CASE_ACTIONS table with IS_HRCO_QA flag as N */
--
-- Insert into SC_HRCQA_CASE_ACTIONS (ACTION_SEQ_ID,ACTION_BY_ID,ACTION_TS,STATUS_CD,CASE_INTRL_ID,ACTION_ID,NEW_CASE_OWNR_ASSGN_ID,CASE_DUE_TS,PREV_CASE_OWNR_ASSGN_ID,IS_HRCO_QA)
-- values (:NEW.ACTION_SEQ_ID, :NEW.ACTION_BY_ID, :NEW.ACTION_TS, :NEW.STATUS_CD, :NEW.CASE_INTRL_ID, :NEW.ACTION_ID, :NEW.NEW_CASE_OWNR_ASSGN_ID, :NEW.CASE_DUE_TS, :NEW.PREV_CASE_OWNR_ASSGN_ID,'N');
END IF;
EXCEPTION
WHEN OTHERS THEN LV_ERRORCODE := SQLCODE;
INSERT INTO KDD_LOGS_MSGS (LOG_DT, LOG_INFO_TX, REMARK_TX)
VALUES (SYSDATE,'ErrorCode - ' || LV_ERRORCODE,'TRG_CHK_HRCQA_CASE_ACTIONS');
END;
You cannot pass a comma-delimited string stored in a single variable to an IN condition and expect it to be parsed as multiple values as it is not.
If you want to use a single variable containing a delimited list then you will need to use string functions to find a sub-string match:
create or replace TRIGGER TRG_CHK_HRCQA_CASE_ACTIONS
AFTER INSERT ON KDD_CASE_ACTIONS
FOR EACH ROW
DECLARE
v_owner_id kdd_review_owner.OWNER_ID%TYPE;
v_audit_user VARCHAR2(1000);
v_evdnse_user VARCHAR2(1000);
LV_ERRORCODE VARCHAR2(1000);
BEGIN
SELECT OWNER_ID
into v_owner_id -- You only need one variable here
from kdd_review_owner
where OWNER_SEQ_ID = :NEW.ACTION_BY_ID;
v_audit_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID, v_owner_id, 'AUDIT');
v_evdnse_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID, v_owner_id, 'EVDNSE');
IF ','||v_audit_user||','||v_evdnse_user||',' LIKE '%,'||:NEW.ACTION_SEQ_ID||',%'
THEN
Insert into SC_HRCQA_CASE_ACTIONS (
ACTION_SEQ_ID, ACTION_BY_ID, ACTION_TS, STATUS_CD, CASE_INTRL_ID,
ACTION_ID, NEW_CASE_OWNR_ASSGN_ID, CASE_DUE_TS, PREV_CASE_OWNR_ASSGN_ID, IS_HRCO_QA
) values (
:NEW.ACTION_SEQ_ID, :NEW.ACTION_BY_ID, :NEW.ACTION_TS, :NEW.STATUS_CD, :NEW.CASE_INTRL_ID,
:NEW.ACTION_ID, :NEW.NEW_CASE_OWNR_ASSGN_ID, :NEW.CASE_DUE_TS, :NEW.PREV_CASE_OWNR_ASSGN_ID,'Y'
);
END IF;
EXCEPTION
WHEN OTHERS THEN LV_ERRORCODE := SQLCODE;
INSERT INTO KDD_LOGS_MSGS (LOG_DT, LOG_INFO_TX, REMARK_TX)
VALUES (SYSDATE,'ErrorCode - ' || LV_ERRORCODE,'TRG_CHK_HRCQA_CASE_ACTIONS');
END;
/
I don't have your tables so I'll try to illustrate it using my own code.
This is what you're doing now: to us (humans), it is obvious that L_NEW_ID (its value is 10) is contained in L_VAR1 (its value is '10,20'). However, Oracle doesn't recognize that and returns "Not OK":
SQL> declare
2 -- does L_NEW_ID exist in L_VAR1 and L_VAR2 (using your code)?
3 l_new_id number := 10;
4 --
5 l_var1 varchar2(20) := '10,20';
6 l_var2 varchar2(20) := '30,40';
7 begin
8 if l_new_id in (l_var1, l_var2) then
9 dbms_output.put_line('OK');
10 else
11 dbms_output.put_line('Not OK');
12 end if;
13 end;
14 /
Not OK
PL/SQL procedure successfully completed.
SQL>
So, what to do? One option is to split variables to rows and then check whether search value (10, right?) exists in such a list of values (rows). The result is - as you can see - "OK":
SQL> declare
2 l_new_id number := 10;
3 --
4 l_var1 varchar2(20) := '10,20';
5 l_var2 varchar2(20) := '30,40';
6 l_cnt number;
7 begin
8 -- count how many times L_NEW_ID exists in L_VAR1 nad L_VAR2
9 select count(*)
10 into l_cnt
11 from (-- split L_VAR1 into rows
12 select regexp_substr(l_var1, '[^,]+', 1, level) val
13 from dual
14 connect by level <= regexp_count(l_var1, ',') + 1
15 union all
16 -- split L_VAR2 into rows
17 select regexp_substr(l_var2, '[^,]+', 1, level)
18 from dual
19 connect by level <= regexp_count(l_var2, ',') + 1
20 ) x
21 where x.val = l_new_id;
22
23 if l_cnt > 0 then
24 dbms_output.put_line('OK');
25 else
26 dbms_output.put_line('Not OK');
27 end if;
28 end;
29 /
OK
PL/SQL procedure successfully completed.
SQL>
Just to verify it, let's change L_NEW_ID value to e.g. 99 and see what happens; as expected, "Not OK" as 99 isn't contained in 10, 20 nor 30, 40.
SQL> l2
2* l_new_id number := 10;
SQL> c/10/99
2* l_new_id number := 99;
SQL> /
Not OK
PL/SQL procedure successfully completed.
SQL>

Create external table from nested directory in oracle

How to create external table from nested directory (directory name will be the column value )
one directory may have multiple sub directory one simple example given below
Country→India→Karnataka→Bengaluru→….data.txt
Country→India→Maharashtra→Mumbai→....data1.txt
Expected Table
Country | India | Karnataka | Bengaluru |…content of data.txt
Country | India | Maharashtra| Mumbai|…content of data1.txt
This is an interesting issue I came across in one of my projects. So, I am going to show basically what I did and all components you need.
Components
Shell Script to run the fill up of the external data into a csv file
Database Objects ( Table External and Final Table )
Clob function and Blob function to treat the external content of the files
Dynamic number of files in final directory implies one row per file
Scenario
Let's imagine I have the following structure
ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' -e 's/-/|/'
.
|-India
|---Karnataka
|-----Bengaluru
|-Spain
|---Catalunya
|-----Barcelona
$ ls -R
.:
India Spain
./India:
Karnataka
./India/Karnataka:
Bengaluru
./India/Karnataka/Bengaluru:
data.txt
./Spain:
Catalunya
./Spain/Catalunya:
Barcelona
./Spain/Catalunya/Barcelona:
data1.txt data2.txt
Following your logic I need an external table as follows
Country|India|Karnataka|Bengaluru|Filename|content of data.txt
Country|Spain|Catalonia|Barcelona|Filename|content of data1.txt
Country|Spain|Catalonia|Barcelona|Filename|content of data2.txt
Contents of the files
pwd
/test/India/Karnataka/Bengaluru
$ cat data.txt
This is an example of text for this city
$ cd ../../../Spain/Catalunya/Barcelona/
$ cat data1.txt
Another example for Catalonia and Barcelona
$ cat data2.txt
Second example for Catalonia and Barcelona
So, our external table would be
CREATE TABLE EXTERNAL_DATA (
TYPE VARCHAR2(4000),
NAME VARCHAR2(4000),
REGION VARCHAR2(4000),
CITY VARCHAR2(4000),
FILENAME VARCHAR2(4000)
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY dir_util_db
ACCESS PARAMETERS (
RECORDS DELIMITED BY newline
FIELD NAMES ALL FILES IGNORE
BADFILE dir_util_db:'ext_tab_inp.bad'
DISCARDFILE dir_util_db:'ext_tab_inp.dsc'
LOGFILE dir_util_db:'ext_tab_inp.log'
FIELDS TERMINATED BY '|'
NOTRIM
MISSING FIELD VALUES ARE NULL
REJECT ROWS WITH ALL NULL
FIELDS
(
TYPE CHAR(4000),
NAME CHAR(4000),
REGION CHAR(4000),
CITY CHAR(4000),
FILENAME CHAR(4000)
)
)
LOCATION ('ext_data_test.csv')
)
REJECT LIMIT UNLIMITED;
Our definitive table will include the blob column.
CREATE TABLE FINAL_DATA (
TYPE VARCHAR2(10),
NAME VARCHAR2(20),
REGION VARCHAR2(20),
CITY VARCHAR2(20),
FILENAME VARCHAR2(20),
DATA BLOB
)
We will need some functions to retrieve the blob file from the filesystem and store its content as blob data. We will also need a function to convert the blob content as clob text.
Step by step
Number of records for the external table
$ find . -name "*.txt" | wc -l
3
Fill up the csv file
$ echo "TYPE|NAME|REGION|CITY|FILENAME" > ext_tab_test.csv
$ for i in $(find . -name "*.txt" -type f -print)
> do
> var=$(echo $i)
> record_type="Country"
> record_name=$(echo $var | awk -F '/' '{print $2}')
> record_region=$(echo $var | awk -F '/' '{print $3}')
> record_city=$(echo $var | awk -F '/' '{print $4}')
> record_file=$(echo $var | awk -F '/' '{print $NF}')
> echo "$record_type|$record_name|$record_region|$record_city|$record_file" >> ext_tab_test.csv
> done
$ cat ext_tab_test.csv
TYPE|NAME|REGION|CITY|FILENAME
Country|India|Karnataka|Bengaluru|data.txt
Country|Spain|Catalunya|Barcelona|data1.txt
Country|Spain|Catalunya|Barcelona|data2.txt
Let's go to the database
Create the directory where the external table is located
SQL> create or replace directory dir_test as '/test' ;
Directory created.
Create the external table
SQL> CREATE TABLE EXTERNAL_DATA (
TYPE VARCHAR2(4000),
NAME VARCHAR2(4000),
REGION VARCHAR2(4000),
CITY VARCHAR2(4000),
FILENAME VARCHAR2(4000)
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY dir_test
ACCESS PARAMETERS (
RECORDS DELIMITED BY newline
FIELD NAMES ALL FILES IGNORE
BADFILE dir_test:'ext_tab_test.bad'
DISCARDFILE dir_test:'ext_tab_test.dsc'
LOGFILE dir_test:'ext_tab_test.log'
FIELDS TERMINATED BY '|'
NOTRIM
MISSING FIELD VALUES ARE NULL
REJECT ROWS WITH ALL NULL
FIELDS
(
TYPE CHAR(4000),
NAME CHAR(4000),
REGION CHAR(4000),
CITY CHAR(4000),
FILENAME CHAR(4000)
)
)
LOCATION ('ext_tab_test.csv')
)
REJECT LIMIT UNLIMITED;
Table created.
SQL> col type for a40
SQL> col name for a40
SQL> col region for a40
SQL> col city for a40
SQL> col filename for a40
SQL> set lines 200
SQL> select * from EXTERNAL_DATA
TYPE NAME REGION CITY FILENAME
---------- ---------------------------------------- ---------------------------------------- ---------------------------------------- ----------------------------------------
Country India Karnataka Bengaluru data.txt
Country Spain Catalunya Barcelona data1.txt
Country Spain Catalunya Barcelona data2.txt
Now we are going to merge the content of this external table into our definitive table.
Create final table
SQL> CREATE TABLE FINAL_DATA
(
TYPE VARCHAR2(10),
NAME VARCHAR2(20),
REGION VARCHAR2(20),
CITY VARCHAR2(20),
FILENAME VARCHAR2(20),
DATA BLOB
) 2 3 4 5 6 7 8 9 ;
Table created.
insert/merge the data from the external table into the final table
SQL> merge into FINAL_DATA target
using ( select * from EXTERNAL_DATA ) source
on ( target.type = source.type and
target.name = source.name and
target.region = source.region and
target.city = source.city
)
2 3 4 5 6 7 8 when not matched then
9 insert ( type, name, region, city, filename )
values
( source.type , source.name , source.region, source.city , source.filename ); 10 11
3 rows merged.
SQL> commit ;
Commit complete.
Now we need the procedure to convert the blob object ( file ) into a blob object and store it into the table
CREATE OR REPLACE FUNCTION os_file_to_blob(p_dir in varchar2 , p_file_name VARCHAR2) RETURN BLOB AS
dest_loc BLOB := empty_blob();
src_loc BFILE := BFILENAME(p_dir, p_file_name);
BEGIN
DBMS_LOB.OPEN(src_loc, DBMS_LOB.LOB_READONLY);
DBMS_LOB.CREATETEMPORARY(
lob_loc => dest_loc
, cache => true
, dur => dbms_lob.session
);
DBMS_LOB.OPEN(dest_loc, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADFROMFILE(
dest_lob => dest_loc
, src_lob => src_loc
, amount => DBMS_LOB.getLength(src_loc));
DBMS_LOB.CLOSE(dest_loc);
DBMS_LOB.CLOSE(src_loc);
RETURN dest_loc;
END os_file_to_blob;
/
And a function to covert the blob into a clob
create or replace function fil_rep.blob_to_char (b blob)
return clob is
v_clob clob;
n number;
v_start pls_integer := 1;
v_buffer pls_integer := 32767;
v_varchar varchar2(32767);
begin
if (b is null)
then
return null;
end if;
if (dbms_lob.getlength(b)=0)
then
return empty_clob();
end if;
dbms_lob.createtemporary(v_clob,true);
for i in 1..ceil(dbms_lob.getlength(b) / v_buffer)
loop
v_varchar := utl_raw.cast_to_varchar2(dbms_lob.substr(b, v_buffer, v_start));
dbms_lob.writeappend(v_clob, length(v_varchar), v_varchar);
v_start := v_start + v_buffer;
end loop;
RETURN v_clob;
end blob_to_char;
/
Create the functions for blob treatment
SQL> CREATE OR REPLACE FUNCTION os_file_to_blob(p_dir in varchar2 , p_file_name VARCHAR2) RETURN BLOB AS
2 dest_loc BLOB := empty_blob();
3 src_loc BFILE := BFILENAME(p_dir, p_file_name);
4 BEGIN
5 DBMS_LOB.OPEN(src_loc, DBMS_LOB.LOB_READONLY);
6 DBMS_LOB.CREATETEMPORARY(
7 lob_loc => dest_loc
8 , cache => true
9 , dur => dbms_lob.session
10 );
11 DBMS_LOB.OPEN(dest_loc, DBMS_LOB.LOB_READWRITE);
12 DBMS_LOB.LOADFROMFILE(
13 dest_lob => dest_loc
14 , src_lob => src_loc
15 , amount => DBMS_LOB.getLength(src_loc));
16 DBMS_LOB.CLOSE(dest_loc);
17 DBMS_LOB.CLOSE(src_loc);
18 RETURN dest_loc;
19* END os_file_to_blob;
/
Function created.
SQL> create or replace function blob_to_char (b blob)
2 return clob is
3 v_clob clob;
4 n number;
5 v_start pls_integer := 1;
6 v_buffer pls_integer := 32767;
7 v_varchar varchar2(32767);
8 begin
9 if (b is null)
10 then
11 return null;
12 end if;
13 if (dbms_lob.getlength(b)=0)
14 then
15 return empty_clob();
16 end if;
17 dbms_lob.createtemporary(v_clob,true);
18 for i in 1..ceil(dbms_lob.getlength(b) / v_buffer)
19 loop
20 v_varchar := utl_raw.cast_to_varchar2(dbms_lob.substr(b, v_buffer, v_start));
21 dbms_lob.writeappend(v_clob, length(v_varchar), v_varchar);
22 v_start := v_start + v_buffer;
23 end loop;
24 RETURN v_clob;
25* end blob_to_char;
/
Function created.
Now, we need to create a directory for each location we have
Directories to be created
create or replace directory dir_01 as '/test/India/Karnataka/Bengaluru' ;
create or replace directory dir_02 as '/test/Spain/Catalunya/Barcelona' ;
Update the blobs for each directory
SQL> DECLARE
v_blob BLOB;
v_file varchar2(40);
BEGIN
for h in
( select filename from final_data where data is null and name = 'India' )
loop
v_file := h.filename;
begin
v_blob := os_file_to_blob ('DIR_01' , v_file);
update final_data set data = v_blob where filename = v_file;
commit;
exception when others then null;
end;
end loop;
end;
/
PL/SQL procedure successfully completed.
SQL> DECLARE
v_blob BLOB;
v_file varchar2(40);
BEGIN
for h in
( select filename from final_data where data is null and name = 'Spain' )
loop
v_file := h.filename;
begin
v_blob := os_file_to_blob ('DIR_02' , v_file);
update final_data set data = v_blob where filename = v_file;
commit;
exception when others then null;
end;
end loop;
end;
/
PL/SQL procedure successfully completed.
Final select
SQL> select a.filename , blob_to_char(a.data) as content from final_data a ;
FILENAME CONTENT
---------------------------------------- --------------------------------------------------------------------------------
data.txt This is an example of text for this city
data1.txt Another example for Catalonia and Barcelona
data2.txt Second example for Catalonia and Barcelona
SQL>
Obviously, if I would to do it, I would put a field in the table called directory_path, therefore I could make the creation of the directories totally automatic.
I did not publish everything I had in mind, just enough for you to get an idea. In my case
All the process is included in a shell script
The process is triggered by dbms_scheduler every single day
In my case, I updating some logfiles for different directories to a final table for some users can check them. The reason is that they have no access to the Filesystem in the server.
I use merge statement to insert/update in the final table, therefore I only upload the content of new or modified files. For that I retrieve in the csv file the file's timestamp.
Please, share your doubts and suggestions.

my trigger wont print what i want it to

so im a student working in Oracle SQL and i wrote a trigger to notify me if the item quantity is under a certain value. my trigger was created without compilation errors but when i test it i do not see the output that i want... I am sure there may be better ways to do certain parts of this but i am concerned mostly with how to get it to print to the command line. also if possible i would like the first if statement to contain a break statement so that the quantity cannot be less than 0. is that done by addint 'BREAK;' before the 'END IF;'?
SQL> #345lowqtytrigger
SQL> CREATE OR REPLACE TRIGGER low_qty_trigger
2 BEFORE INSERT OR UPDATE OF vnd_itm_qty ON Vending_Machine_Item
3 FOR EACH ROW
4 DECLARE
5 tempstr varchar2(1000);
6 name varchar2(1000);
7 vndadd varchar2(1000);
8 tempint int;
9 cursor itm_des_cursor is
10 SELECT itm_des FROM item WHERE itm_id = item.itm_id;
11 cursor vnd_addr_cursor is
12 SELECT vnd_addr FROM Vending_Machine WHERE vnd_id = Vending_Machine.vnd_id;
13 BEGIN
14 open itm_des_cursor;
15 fetch itm_des_cursor into name;
16 open vnd_addr_cursor;
17 fetch vnd_addr_cursor into vndadd;
18 IF :NEW.vnd_itm_qty < 0 THEN
19 tempstr := concat(:new.vnd_id,concat(' can not have less than 0 quantity for ',name));
20 dbms_output.put_line(tempstr);
21 end if;
22 tempint := :new.vnd_itm_qty_max*.15;
23 IF
24 :NEW.vnd_itm_qty <= tempint
25 and
26 :NEW.vnd_itm_qty >= 0
27 THEN
28 tempstr := concat('There will be less than ',
29 concat(to_char(tempint),
30 concat(' of ',
31 concat(name,
32 concat(' in Vending Machine ',
33 concat(to_char(:new.vnd_id),
34 concat(' located at ',vndadd)))))));
35 dbms_output.put_line(tempstr);
36 END IF;
37 close itm_des_cursor;
38 close vnd_addr_cursor;
39 END;
40 /
Trigger created.
SQL> SET ECHO OFF;
No errors.
SQL> spool off;
TEST CASE
SQL> update vending_machine_item set vnd_itm_qty = 1 where vnd_id = 956 and itm_id = 193;
1 row updated.
How are you testing this? It has to be in an interactive session to see any output generated by dbms_output. Try an update on vending_machine_item from a sql*plus session with set serveroutput on.

Replacing text in a BLOB Column

In one of our tables we have a HUGEBLOB Column (Column name is DYNAMIC_DATA) which holding an XML data. What I need to do is updating a certain part of the text from within this BLOB.
I've tried this query:
UPDATE ape1_item_version
SET DYNAMIC_DATA = REPLACE (DYNAMIC_DATA,'Single period','Single period period set1')
WHERE name = 'PRIT ALL POOL for Duration Telephony 10_NA_G_V_H_N_Z2'
But I get the following error:
ORA-00932: inconsistent datatypes: expected NUMBER got BLOB
How can I execute REPLACE on the BLOB ?
REPLACE works on the following datatypes:
Both search_string and replacement_string, as well as char, can be any of the data types CHAR, VARCHAR2, NCHAR, NVARCHAR2, CLOB, or NCLOB.
You have chosen to store character data as a collection of bytes (BLOB). These can not be worked on directly because a BLOB has no context and is only a very very big number. It can't be converted to characters without your input: you need its character set to convert binary data to text.
You'll have to either code the function REPLACE yourself (using DBMS_LOB.instr for instance) or convert your data to a workable CLOB and use standard functions on the CLOB.
I would advise strongly to change the datatype of your column. This will prevent any further character set conversion error you will likely run into in the future.
If you really want to work with blobs, use functions like these:
SQL> CREATE OR REPLACE FUNCTION convert_to_clob(l_blob BLOB) RETURN CLOB IS
2 l_clob CLOB;
3 l_dest_offset NUMBER := 1;
4 l_src_offset NUMBER := 1;
5 l_lang_context NUMBER := dbms_lob.default_lang_ctx;
6 l_warning NUMBER;
7 BEGIN
8 dbms_lob.createtemporary(l_clob, TRUE);
9 dbms_lob.converttoclob(dest_lob => l_clob,
10 src_blob => l_blob,
11 amount => dbms_lob.lobmaxsize,
12 dest_offset => l_dest_offset,
13 src_offset => l_src_offset,
14 blob_csid => nls_charset_id('AL32UTF8'),
15 lang_context => l_lang_context,
16 warning => l_warning);
17 RETURN l_clob;
18 END convert_to_clob;
19 /
Function created
SQL> CREATE OR REPLACE FUNCTION convert_to_blob(l_clob CLOB) RETURN BLOB IS
2 l_blob BLOB;
3 l_dest_offset NUMBER := 1;
4 l_src_offset NUMBER := 1;
5 l_lang_context NUMBER := dbms_lob.default_lang_ctx;
6 l_warning NUMBER;
7 BEGIN
8 dbms_lob.createtemporary(l_blob, TRUE);
9 dbms_lob.converttoblob(dest_lob => l_blob,
10 src_clob => l_clob,
11 amount => dbms_lob.lobmaxsize,
12 dest_offset => l_dest_offset,
13 src_offset => l_src_offset,
14 blob_csid => nls_charset_id('AL32UTF8'),
15 lang_context => l_lang_context,
16 warning => l_warning);
17 RETURN l_blob;
18 END convert_to_blob;
19 /
Function created
You can call these functions directly from SQL:
SQL> UPDATE ape1_item_version
2 SET DYNAMIC_DATA = convert_to_blob(
3 REPLACE(convert_to_clob(DYNAMIC_DATA),
4 'Single period',
5 'Single period period set1')
6 )
7 WHERE NAME = 'PRIT ALL POOL for Duration Telephony 10_NA_G_V_H_N_Z2';
1 row updated
We can use something like the below query also with Oracle 11 and above if the blob object is of text.
`UPDATE table_name
SET text_blob_column-name = UTL_RAW.CAST_TO_RAW(
REPLACE(UTL_RAW.CAST_TO_VARCHAR2(text_blob_column-name),
'<existing value>',
'<value to update>')
)
WHERE where_clause_Column-name='171';`

COUNT function for files?

Is it possible to use COUNT in some way that will give me the number of tuples that are in a .sql file? I tried using it in a query with the file name like this:
SELECT COUNT(*) FROM #q65b;
It tells me that the table is invalid, which I understand because it isn't a table, q65b is a file with a query saved in it. I'm trying to compare the number of rows in q65b to a view that I have created. Is this possible or do I just have to run the query and check the number of rows at the bottom?
Thanks
You can do this in SQL*Plus. For example:
Create the text file, containing the query (note: no semicolon!):
select * from dual
Save it in a file, e.g. myqueryfile.txt, to the folder accessible from your SQL*Plus session.
You can now call this from within another SQL query - but make sure the # as at the start of a line, e.g.:
SQL> select * from (
2 #myqueryfile.txt
3 );
D
-
X
I don't personally use this feature much, however.
Here is one approach. It's a function which reads a file in a directory, wraps the contents in a select count(*) from ( .... ) construct and executes the resultant statement.
1 create or replace function get_cnt
2 ( p_file in varchar2 )
3 return number
4 as
5 n pls_integer;
6 stmt varchar2(32767);
7 f_line varchar2(255);
8 fh utl_file.file_type;
9 begin
10 stmt := 'select count(*) from (';
11 fh := utl_file.fopen('SQL_SCRIPTS', p_file, 'R');
12 loop
13 utl_file.get_line(fh, f_line );
14 if f_line is null then exit;
15 elsif f_line = '/' then exit;
16 else stmt := stmt ||chr(10)||f_line;
17 end if;
18 end loop;
19 stmt := stmt || ')';
20 execute immediate stmt into n;
21 return n;
22* end get_cnt;
SQL>
Here is the contents of a sql file:
select * from emp
/
~
~
~
"scripts/q_emp.sql" 3L, 21C
And here is how the script runs:
SQL> select get_cnt ('q_emp.sql') from dual
2 /
GET_CNT('Q_EMP.SQL')
--------------------
14
SQL>
So it works. Obviously what I have posted is just a proof of concept. You will need to include lots of error handling for the UTL_FILE aspects - it's a package which can throw lots of exceptions - and probably some safety checking of the script that gets passed.