Remove special Character from complete table - sql

I have 5 to 6 tables in which i am inserting data using Excel file but i don't know whats the issue but for some data special character are adding and that is giving me problem. So is their any way to update complete table by passing the only Table_name in some procedure.currently i am using one function to update the column but i want something more easy to use (for my other team member).
I am using below function .
create or replace FUNCTION pfm_on_varchar(
p_str IN VARCHAR2)
RETURN VARCHAR2
IS
o_str VARCHAR2(4096) := '';
asc_val NUMBER;
BEGIN
FOR I IN 1 .. LENGTH(p_str)
LOOP
asc_val := ascii(SUBSTR(p_str,i,1));
IF ((asc_val BETWEEN 48 AND 57) OR (asc_val BETWEEN 65 AND 90) OR (asc_val BETWEEN 97 AND 122)) THEN
o_str := o_str || chr(asc_val);
END IF;
END LOOP;
RETURN o_str;
END pfm_on_varchar;
and how can I pass table name and column name in a procedure to update the table ?I am trying this code
CREATE OR REPLACE PROCEDURE removeSpecialChar(table_new IN varchar2, column_new IN varchar2)
AS
BEGIN
update table_new
set column_new = PFM_ON_VARCHAR(column_new);
end removeSpecialChar;
/
Error I am getting
Error(4,3): PL/SQL: SQL Statement ignored
Error(4,10): PL/SQL: ORA-00942: table or view does not exist

As mentioned in the comments , you can try using SQL as below:
Update table1
set columnname = case
when ascii(columnname) between 48 and 54
then replace (columnname,ascii(48),'')
end
where <condition> ;

update table
set columnName = regexp_replace('This is a test. $%&^*&* ', '[^A-Za-z .?!]', '')
Add all the characters which you don't want removed in the [^A-Za-z .]
For the procedure you could do a dynamic sql. Otherwise it will not pick the variable up. Like this:
CREATE OR REPLACE PROCEDURE removeSpecialChar(table_new IN varchar2, column_new IN varchar2)
AS
BEGIN
execute immediate 'update '||table_new||
' set '||column_new||' = PFM_ON_VARCHAR('||column_new||')';
end removeSpecialChar;
/

Related

Passing Multiple Values To PL/SQL Input

I have an Oracle package which contains a function. This function has 3 inputs. I need to pass multiple values to each input. I could automate a process which runs it multiple times with each combination of variables but I would like to only make one call to the database.
Simplified Code
declare
ln_ret number;
begin
ln_ret := dbo.pkg_rpa.mis_run_script (
'%2020%',
'111','222','333','444',
'1234','2345','6192','1204'
);
dbms_output.put_line('ln_ret=' || t.t (ln_ret));
end;
CREATE OR REPLACE
package dbo.pkg_rpa IS
function mis_run_script (
p_input_dt in varchar2,
p_hospital_id in varchar2,
p_procedure_code in varchar2) RETURN number;
end PKG_RPA;
/
CREATE OR REPLACE
PACKAGE BODY dbo.pkg_rpa IS
function mis_run_claim_assessment_script (
p_input_dt in varchar2,
p_hospital_id in varchar2,
p_procedure_code in varchar2
)
Begin
for i in (select table_name from user_tables where lower(table_name) = 'temp_rpa') loop
execute immediate 'drop table temp_rpa';
end loop;
execute immediate ' create table temp_rpa as select distinct ci.claim_id, count(ci.receipt_id) as count_receipts,
sum(ci.billed_amount) as total_billed_amount, count(*) as claim_items
from claim_item ci left join claim_header ch on ch.claim_id = ci.claim_id
left join cd_hos ho on ho.hospital_id = ci.hospital_id
left join claim_type_header cl on cl.claim_id = ci.claim_id
where cl.claim_status is null and ch.deleted_flag is null
and ch.input_dt like p_input_dt
and ci.hospital_id in (p_hospital_id)
and (ci.claim_id, NVL(ci.claim_item_id,0)) in (select claim_id, NVL(claim_item_id,0) from cd_roc_claim_item
where procedure_code in (p_procedure_code))
and (ci.claim_id, NVL(ci.claim_item_id,0)) not in (select claim_id, NVL(claim_item_id,0) from cd_roc_claim_item
where procedure_code not in (p_procedure_code))
group by ci.claim_id
having sum(case when ci.service_type_id is null then 1 end) = 1)';
End;
end mis_run_script;
end PKG_RPA;
/
Pass it with quoted string (Q'<delimeter><your_actual_string><delimeter>') as follows:
begin
ln_ret := dbo.pkg_rpa.mis_run_script (
'%2020%',
Q'#'111','222','333','444'#',
Q'#'1234','2345','6192','1204'#'
);
dbms_output.put_line('ln_ret=' || t.t (ln_ret));
end;
What you could do is using an associative array as input type. Instead of varchar2, use dbms_sql.varchar2a as date type for the 2nd and 3rd arguments.
But if the arguments are related to each other, let's say
p_hospital_id '111' belongs to procedure code '1234'
p_hospital_id '222' belongs to procedure code '2345'
etc.
I think you would want to create a custom record type, create a table type of the record type and use that as an parameter.
Your arguments become p_hospital_ids in dbms_sql.varchar2a in both the package specification and the package body.
In you code, you would have to loop over it and instead of dropping the table and recreate it each time, you drop it once at the start and add data within the loop;
truncate table; --alternative drop and create
for i in 1 .. p_hospital_ids.count loop
insert into temp_rpa
select <columns>
from claim_item ci
......
and ci.hospital_id = p_hospital_ids[i]
end loop
You may want to refer to the below example which is taken from Oracle Website. Hope it helps.
CREATE OR REPLACE TYPE nt_type IS TABLE OF NUMBER;
/
CREATE OR REPLACE PROCEDURE print_nt (nt nt_type) AUTHID DEFINER IS
i NUMBER;
BEGIN
i := nt.FIRST;
IF i IS NULL THEN
DBMS_OUTPUT.PUT_LINE('nt is empty');
ELSE
WHILE i IS NOT NULL LOOP
DBMS_OUTPUT.PUT('nt.(' || i || ') = ');
DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(nt(i)), 'NULL'));
i := nt.NEXT(i);
END LOOP;
END IF;
DBMS_OUTPUT.PUT_LINE('---');
END print_nt;
/
DECLARE
nt nt_type := nt_type(); -- nested table variable initialized to empty
BEGIN
print_nt(nt);
nt := nt_type(90, 9, 29, 58);
print_nt(nt);
END;
/
You don't need dynamic SQL at all.
CREATE OR REPLACE TYPE NUMBER_TABLE_TYPE AS TABLE OF NUMBER;
CREATE OR REPLACE TYPE VARCHAR_TABLE_TYPE AS TABLE OF VARCHAR2(1000);
function mis_run_claim_assessment_script (
p_input_dt in varchar2,
p_hospital_id in NUMBER_TABLE_TYPE, -- why on earth do you put numbers as strings?
p_procedure_code in VARCHAR_TABLE_TYPE
) RETURN ??? AS
INSERT INTO temp_rpa (...)
SELECT ...
FROM ...
WHERE ch.input_dt like p_input_dt
AND ci.hospital_id MEMBER OF p_hospital_id
AND procedure_code MEMBER OF p_procedure_code ;
RETURN ???
END;
ln_ret := mis_run_claim_assessment_script(
'%2020%',
NUMBER_TABLE_TYPE(111, 222, 333, 444),
VARCHAR_TABLE_TYPE('not','clear','in','your','question')
);

DB Procedure to Dynamically update DB columns

Requirement is to update in db column only those column which are edited from frontend.
Logic i am using is to send 2 arrays from java code to DB procedire.
1st array is: Column_array which contains column names which have to be updated.
&
2nd array : value_array which contains the values of columns respoctive to the column_array.
From JAVA:
Array value_array = ((OracleConnection) dbConnection).createOracleArray("STRING_ARRAY",
valueList.toArray());
Array param_array = ((OracleConnection) dbConnection).createOracleArray("STRING_ARRAY",
paramList.toArray());
stmt = dbConnection.prepareCall(SqlConstants.UPDATE_SUBSCRIBER_CONFIG_IN_BOLTES);//
stmt.setLong(1, 3628);
stmt.setLong(2, 3629);
stmt.setLong(3, 3632);
stmt.setArray(4, param_array);
stmt.setArray(5, value_array);
int count = stmt.executeUpdate();
NOW at DB side:
how can i iterate over this list to update and set this in SET clause???
PROCEDURE update_subscriber_config (
p_app_id VARCHAR2,
p_service_id VARCHAR2,
p_pubsub_id VARCHAR2,
column_list string_array,
value_list string_array
)
AS
BEGIN
FOR a IN 1..column_list.count LOOP
update bolt_oracle_pubsub_config set
column_list(a)=value_list(a),
...how to do iteration here???
where APP_ID = p_app_id AND SERVICE_ID = p_service_id AND PUBSUB_ID = p_pubsub_id;
END LOOP;
END update_subscriber_config;
PLEASE HELP.
I found the solution which was simpler than array iteration.
Solution is to use COALESCE method.
PROCEDURE TEST (
p_app_id NUMBER,
p_service_id NUMBER,
p_pubsub_id NUMBER,
p_pubsub_name VARCHAR2,
p_host VARCHAR2,
p_user_name VARCHAR2,
p_auth_key VARCHAR2
)
AS
BEGIN
update bolt_elastic_pubsub_config set
PUBSUB_NAME = coalesce(p_pubsub_name, PUBSUB_NAME),
HOST = coalesce(p_host, HOST),
USER_NAME = coalesce(p_user_name, USER_NAME),
AUTH_KEY = coalesce(p_auth_key, AUTH_KEY)
where APP_ID = p_app_id AND SERVICE_ID = p_service_id AND PUBSUB_ID = p_pubsub_id;
END TEST;
You have to use dynamic SQL to assemble bespoke update statements. It will look something like this:
PROCEDURE update_subscriber_config (
p_app_id VARCHAR2,
p_service_id VARCHAR2,
p_pubsub_id VARCHAR2,
column_list string_array,
value_list string_array
)
AS
stmt varchar2(32767);
BEGIN
stmt := 'update bolt_oracle_pubsub_config set ';
FOR a IN 1..column_list.count LOOP
if a != 1 then
stmt := stmt ||', ';
end if;
stmt := stmt || column_list(a) ||'=''' ||value_list(a)||'''';
END LOOP;
stmt := stmt ||
' where APP_ID = :p1 AND SERVICE_ID = :p2 AND PUBSUB_ID = :p3';
execute immediate stmt using p_app_id , p_service_id , p_pubsub_id;
END update_subscriber_config;
This is a very crude implemnentation, as it assumes all the passed columns can be treated as strings. If your table has numeric or date columns you should think about handling data conversion, because that could become a problem.
Dynamic SQL is hard because it turns compilation errors into runtime errors. In your case you have a piece of code which potentially executes a different update statement every time you call it. So testing this procedure is a total nightmare. This means you have a heavy dependency on the front end passing arrays with valid contents.
Perhaps you could try avoiding one of the arrays which are the column array as Oracle SQL would tolerate if the value is null.
The below is the pseudo code.
PROCEDURE update_subscriber_config (p_app_id VARCHAR2,
p_service_id VARCHAR2,
p_pubsub_id VARCHAR2,
p_first_column VARCHAR2,
p_second_column VARCHAR2,
--column_list string_array,
i_array IN my_array_type)
AS
BEGIN
FORALL i IN 1 .. i_array.COUNT
UPDATE bolt_oracle_pubsub_config
SET your_first_column =
TREAT (i_array (i) AS my_array_type).column_array_name,
your_second_column =
TREAT (i_array (i) AS my_array_type).second_column_array_name
WHERE APP_ID = p_app_id
AND SERVICE_ID = p_service_id
AND PUBSUB_ID = p_pubsub_id;
END update_subscriber_config;
If any of the columns which are stated in the update SQL is empty, it would be null or empty in the table after executing the update. Having said that, ensure all NOT NULL or mandatory columns are populated.
I would reckon to avoid the dynamic SQL update due to error-prone. If you achieve the results without using dynamic SQL, then why the hassle with dynamic SQL update.

how to make variable hold the value taken from one procedure to be used in another procedure in plsql

I have a package with two procedures
in same package.
p_set_values (pn_id_number IN number)
p_get_values (prc_records OUT sys_refcursor)
p_set_values (pn_id_number IN number)
inserts data in to my_table where id_number = pn_id_number
p_get_values (prc_records OUT sys_refcursor) - this procedure has to select the value from my_table where id_number = pn_id_number (Note: same id number which is used to insert the value ,now used to set the values inserted.)
I have declared package level variable and assigned as ln_id_number = pn_id_number.
Now when using this 'ln_id_number' in second procedure, the value of ln_id_number is nothing when checked using dbms_putput.putline(ln_id_number)
Please help me to do this,
Procedures are as follows
CREATE OR REPLACE PACKAGE BODY pck_exit_info
IS
ln_id_number po010.polref%TYPE;
PROCEDURE p_set_values(pn_id_number IN number
,pv_exit_option IN VARCHAR2)
IS
ln_system_date cs340.sysdte%TYPE;
lv_audaplcde package_audits.audit_application_code%TYPE;
ln_audstfno package_audits.audit_staff_number%TYPE;
ln_audupdid NUMBER;
lv_exit_option VARCHAR2(1);
BEGIN
ln_system_date := pck_system_context.f_get_system_date;
ln_id_number := pn_id_number;
dbms_output.put_line(ln_id_number);
SELECT AUDUPDID_SEQ.NEXTVAL INTO ln_audupdid FROM DUAL;
IF pv_exit_option = 'LE' THEN
lv_exit_option := 'L';
ELSE
lv_exit_option := 'S';
END IF;
SELECT audit_application_code,audit_staff_number
INTO lv_audaplcde,ln_audstfno
FROM package_audits
WHERE process = 'PCK_LEAVERS'
AND subprocess ='DEFAULT';
INSERT INTO my_table
VALUES(pn_id_number
,ln_system_date
,lv_exit_option
,ln_audupdid
,ln_system_date
,lv_audaplcde
,ln_audstfno);
END set_employer_exit_info;
PROCEDURE p_get_values(prc_records OUT SYS_REFCURSOR) IS
BEGIN
/*dbms_output.put_line('ID inside get employer');
dbms_output.put_line(ln_id_number);*/
OPEN prc_records FOR
SELECT *
FROM my_table
WHERE polref = ln_id_number;
-- CLOSE prc_policy;
END get_employer_exit_info;
END pck_exit_info;

Get Maximum Length allowed in column | Oracle

How to get the Max and Min length allowed in column of varchar2.
I have to test for incoming data from the temp table which is coming from some remote db. And each row value is to be tested for specific columns that it has maximum or minimum value which can be set into the column.
So I was to get the column specs using its schema details. I did make a proc for that:
PROCEDURE CHK_COL_LEN(VAL IN VARCHAR2,
MAX_LEN IN NUMBER :=4000,
MIN_LEN IN NUMBER :=0,
LEN_OUT OUT VARCHAR2)
IS
BEGIN
IF LENGTH(VAL)<MIN_LEN THEN
LEN_OUT := 'ERROR';
RETURN;
ELSIF LENGTH(VAL)>MAX_LEN THEN
LEN_OUT := 'ERROR';
RETURN;
ELSE
LEN_OUT := 'SUCCESS';
RETURN;
END IF;
END;
END CHK_COL_LEN;
But the problem is, it is not reusable and is a bit hardcoded. I have to explicitly send MAX and MIN value for each value along with the data to be checked.
So at the proc call, it's something like:
CHK_COL_LEN(EMP_CURSOR.EMP_ID, 5, 1, LEN_ERROR_MSG);
I instead want something like: (If something like this exist!)
CHK_COL_LEN(EMP_CURSOR.EMP_ID,
EMP.COLUMN_NAME%MAX_LENGTH,
EMP.COLUMN_NAME%MIN_LENGTH,
LEN_ERROR_MSG)
Thanks in advance.
EDIT
select max(length(col)) from table;
This is a solution, but again I will have to run this query each time to set the two variables for MAX and MIN value. And running extra two queries for each value and then setting 2 variables will cost be significant lose in performance when in have about 32 tables, each with 5-8 varchar2 columns and average rows of about 40k-50k in each table
You can query the table 'user_tab_columns table' to retrieve metadata information of a specific table:
SELECT
COLUMN_NAME, DATA_LENGTH, DATA_PRECISION
FROM
user_tab_columns
WHERE
t.table_name IN ('<YOURTABLE>');
with this information you can query the metadata directly in your stored procedure:
...
SELECT
CHAR_LENGTH INTO max_length
FROM
user_tab_columns
WHERE
table_name = '<YOURTABLE>' AND COLUMN_NAME = '<YOURCOLUMN>';
...
Exmple Procedure to get max length of table/column:
create or replace PROCEDURE GET_MAX_LENGTH_OF_COLUMN(
tableName IN VARCHAR2,
columnName IN VARCHAR2,
MAX_LENGTH OUT VARCHAR2)
IS
BEGIN
SELECT CHAR_LENGTH INTO MAX_LENGTH
FROM user_tab_columns
WHERE table_name = tableName AND COLUMN_NAME = columnName;
END GET_MAX_LENGTH_OF_COLUMN;
Try creating your procedure like this:
create or replace procedure Checking_size(column_name varchar2,columnvalue varchar2,state out varchar2) is
begin
execute immediate 'declare
z '||column_name||'%type;
begin
z:=:param2;
end;' using columnvalue;
state:='OK';
exception when value_error then
state:='NOT OK';
end;
As you can see i simulate an error assignment. If columnvalue length is bigger than the column i pass as column_name it will throws value_error exception and return NOT OK, else return OK.
For example, if your_table.your_column refer to a column with length (3) then return NOT OK.
declare
state varchar2(10);
begin
Checking_size('your_table.your_column','12345',state);
dbms_output.put_line(state);
end;
If the list of tables is not much you can specify the MIN Value using CHECK Constraint on the table.
Any DML on the table would automatically fail if it exceeds length assigned to that column.
CREATE TABLE suppliers
(
supplier_id numeric(4),
supplier_name varchar2(50),
CONSTRAINT check_supplier_id
CHECK (length(supplier_name) > 5 )
);

Error in Parameters of Stored Procedure when Dynamic SQL is used

I am using Oracle Sql Developer to write a Stored Proc, which accepts a list of values separated by "," (dynamic sql) and use this variable in "in" clause to fire a query from a table.
But i am ending up with an error at
#p_case_nbr varchar2(100),
Error(4,5): PLS-00103: Encountered the symbol "#" when expecting one of the following: current delete exists prior
The SP used:
create or replace
procedure TEST_PROC
(
#p_case_nbr varchar2(100),
p_out_case_nbr out varchar2
)
as
cursor test_cur is
select t.COLUMN1
from test_table t
where t.column1 in (#p_case_nbr);
test_cur_line test_cur%ROWTYPE;
begin
p_out_case_nbr := null;
open test_cur;
LOOP
FETCH test_cur INTO test_cur_line;
EXIT WHEN test_cur%NOTFOUND;
p_out_case_nbr := p_out_case_nbr || ' ' || test_cur_line.COLUMN1;
dbms_output.put_line(test_cur_line.COLUMN1);
end loop;
close test_cur;
dbms_output.put_line('Final Value ' || p_out_case_nbr);
dbms_output.put_line('SP Completed Succesfully');
end;
In ORACLE no need of giving # before varibles
Just try to remove the # from #p_case_nbr where ever specified and try once again
and also no need of specifying the size of the parameter like #p_case_nbr varchar2(100)
just give #p_case_nbr varchar