Searching data from a table using stored procedure in oracle by passing tablename as a parameter - oracle

This procedure is not working properly.
create or replace procedure bank_search_sp
(
p_tablename in varchar2,
p_searchname in varchar2,
p_bankcode out varchar2,
p_bankname out varchar2,
p_dist_code out number
)
as
v_tem varchar2(5000);
begin
v_tem :='select bankcode,bankname,dist_code from ' || UPPER (p_tablename) || '
where bankname like '''|| p_searchname||'';
execute immediate v_tem into p_bankcode,p_bankname,p_dist_code using p_searchname ;
commit;
end bank_search_sp;
the Procedure is getting created but i dont know what actually happens when it was executed ,This is the error shown
ORA-01756: quoted string not properly terminated
ORA-06512: at "PENSIONS.BANK_SEARCH_SP", line 14
ORA-06512: at line 1

Trailing commas are missing from this line:
v_tem :='select bankcode,bankname,dist_code from ' || UPPER (p_tablename) || '
where bankname like '''|| p_searchname||'''';
Notice the 4 commas at the end of the string fragment. The first opens the string, the last closes it, the middle two insert a single comma that will be the closing comma for the search expression).
The UPPER() function is not necessary; Oracle does not care the casing of the object names.
I am not sure if having multiple tables with the same structure is the best solution. Would not it be better to have one table only with an indexed column indicating the difference between banks?

Related

VARCHAR2(32767) not able to handle strings in stored procedure

I am concatenating string using cursor (to form query to execute later). Here, the query that will be formed is going to be way bigger that what VARCHAR2(32767) can handle. There fore, I am getting error on proc execution - ORA-06502: PL/SQL: numeric or value error: character string buffer too small.
I used CLOB data type as well bu got error ORA-06502: PL/SQL: numeric or value error.
My code is here below:
CREATE OR REPLACE PROCEDURE sp_Market
IS
Names VARCHAR2(32767);
BEGIN
DECLARE CURSOR cur IS ('Select ID, Order_of, field_name
FROM pld_medicare_config');
BEGIN
FOR i IN cur
LOOP
Names := Names || i.sqql;
END LOOP;
dbms_output.put_line(Names);
END;
END sp_Market;
How can I handle my string of queries and what data type is there to accomplish the task?
CLOB is OK (as far as I can tell); I doubt queries you store in there are that big.
Remove dbms_output.put_line call from the procedure; I suspect it is the one that raises the error.
I'm not sure how you got any runtime error, as your procedure won't compile.
The valid PL/SQL version would look something like this:
create or replace procedure sp_market is
names varchar2(32767);
begin
for r in (
select id, order_of, field_name
from pld_medicare_config
)
loop
names := names || ' ' || r.field_name;
end loop;
names := ltrim(names);
dbms_output.put_line(names);
end sp_market;
If names needs to be longer, change the datatype to clob.
Use the CLOB datatype and append data using the dbms_lob.writeappend procedure. This is the reference (Oracle 18c).
The error probably origins with the dbms_output.put_line call. The procedure is defined for varchar2 arguments only which means that an implicit conversion takes place during the call. It will fail for clob contents longer than 32767 chars/bytes.
Alternatively you may declare a collection over varchar2(4000) and fill the collection elements sequentially:
CREATE OR REPLACE PROCEDURE sp_Market
IS
TYPE tLongString IS TABLE OF VARCHAR2(4000) INDEX BY BINARY_INTEGER;
cNames tLongString;
BEGIN
DECLARE CURSOR cur IS Select ID, Order_of, field_name, sqql FROM pld_medicare_config;
BEGIN
FOR i IN cur
LOOP
cNames(cNames.COUNT+1) := i.sqql;
END LOOP;
END;
END sp_Market;
Note
Rectified code, will compile now.

PLS-00103 creating an external table with dynamic SQL

I'm trying to dynamically create an external table but I'm getting error message PLS-00103: Encountered the symbol "EXTERNAL". I'm also using ora_hash in the external table definition; please let me know it is the right way to get the ora_hash value.
create or replace procedure CHECKTABLEEXIST1 (p_tab_name in varchar2,DATAFILE in varchar2) --user_tables.table_name%type)
is
tab_name varchar2(100) := p_tab_name;
n Number(3);
ext_table varchar(100) := tab_name|| ' as select * from xyz WHERE 1=0';
begin
select count(*) into n from tab where TName=upper(tab_name);
--dbms_output.put_line(n);
if n=0 then
execute immediate 'create table ' || ext_table ;
else
execute immediate 'drop table ' || tab_name;
execute immediate 'create table ' || ext_table;
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY DE_DUBFILE
ACCESS PARAMETER
(
RECORDS DELIMITED BY NEWLINE
CHARACTERSET US7ASCII
BADFILE 'UPLOAD':'p_tab_name.bad'
DISCARDFILE 'UPLOAD':'p_tab_name.dis'
LOGFILE 'UPLOAD':'p_tab_name.log'
FILEDS TERMINATED BY ','
optionally enclosed by '"'
TRAILING NULLCOLS
MISSING FIELD VALUES ARE NULL
(
t1 ,t2,t3,t4,t5 date "YYYYMMDD" ,t6,t7,
t8 ,t9 ,
DETL_CLMNS_HASH "ORA_HASH( :t4||:t7 )",
KEY_CLMNS_HASH "ORA_HASH(:t1||:t2||:t5)", t10,t11)
)
LOCATION (DATAFILE)
);
end if;
end;
I'm getting error message :
LINE/COL ERROR
-------- -----------------------------------------------------------------
16/14 PLS-00103: Encountered the symbol "EXTERNAL" when expecting one o
f the following:
:= . ( # % ;
Everything from ORGANIZATION onwards is being seen as PL/SQL code, not part of your dynamic SQL statement. You're appending the table name to the create table but then not appending the rest as part of that statement string. You need to do something like:
execute immediate 'create table ' || p_tab_name || '
( /* put column names and types here */ )
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY DE_DUBFILE
ACCESS PARAMETERS
(
RECORDS DELIMITED BY NEWLINE
CHARACTERSET US7ASCII
BADFILE UPLOAD:''' || p_tab_name || '.bad''
DISCARDFILE UPLOAD:''' || p_tab_name || '.dis''
LOGFILE UPLOAD:''' || p_tab_name || '.log''
FIELDS TERMINATED BY '',''
optionally enclosed by ''"''
MISSING FIELD VALUES ARE NULL
(
t1 ,t2,t3,t4,t5 date mask "YYYYMMDD" ,t6,t7,
t8 ,t9, t10,t11
)
LOCATION (''' || DATAFILE || ''')
)';
In the first line the terminating semicolon has been replaced with concatenation of a new string literal. The references to variables p_tab_name and DATAFILE have to be broken out from that literal too, requiring more single quotes and concatenation; and the single quotes that are actually part of the statement need to be escaped by doubling them up. There were various other quotes missing as well. What's shown should now run.
I've also change the table name being used to just p_tab_name, but you need to specify the column names and data types explicitly. It doesn't make sense to use as select * ... for an external table. That isn't legal syntax, either before organization or after the rest if the current statement. I suppose you could extract that information from all_tab_columns and build that part dynamically too, but if you're basing it on a fixed table you ought to know those anyway.
Your logic for dropping/creating is off too - I think you just want:
if n>0 then
execute immediate 'drop table ' || p_tab_name;
end if;
execute immediate 'create table ' || p_tab_name || '
...
... so you don't have to repeat the create statement in both branches.
I've also corrected a couple of other mistakes; PARAMETERS rather then PARAMETER; FIELDS rather then FILEDS; removed TRAILING NULLCOLS. Try to execite the command as static SQL before converting it to dynamic. There may still be other issues.
And I've removed the last two calculated columns:
DETL_CLMNS_HASH "ORA_HASH( :t4||:t7 )",
KEY_CLMNS_HASH "ORA_HASH(:t1||:t2||:t5)")
The ORACLE_LOADER driver doesn't allow manipulations like that; SQL*Loader does but they are not exactly the same. You also can't define virtual columns on an external table. If you're using this as a staging table to load data into another (real) table then you can calculate those hashes during the transfer; otherwise you can create a view over this external table which includes the calculated columns.

Use execute immediate in procedure for DML and passing character value in parameter

I have a delete procedure which is taking table name and some values to delete record from that table, hence I have created a procedure with execute immediate which is forming the delete query by taking the parameter and delete.
But when ever I am passing the char value in the parameter it is getting error :
invalid identifier
as query formed with out single quote for the character value. Please let me know how can I pass char value in the procedure to form a string correctly.
Below is the procedure:
CREATE OR replace PROCEDURE Prd_delete(p_tbl_name IN VARCHAR2,
p_sys VARCHAR2,
p_b_id VARCHAR2,
p_c_date NUMBER)
IS
dlt_query VARCHAR2(200);
BEGIN
dlt_query := 'delete from '
||p_tbl_name
||' where system='
||p_sys
|| ' And batch_id='
||p_b_id
|| ' And cobdate='
||p_c_date;
--dbms_output.put_line(dlt_query);
EXECUTE IMMEDIATE dlt_query;
END;
/
Below is the running command :
exec prd_delete ('TBL_HIST_DATA','M','N1',20141205);
Below is the error :
ORA-00904:"N1" invalid identifier.
How to pass this value correctly ? please suggest.
At first place, why do you need PL/SQL for the DELETE. You could do it in plain SQL.
Why is P_C_DATE a NUMBER, What data type is cobdate COLUMN. A date should always be a DATE. If the column data type is DATE, then you will run into more errors. Always pay attention to declaring correct data types.
With dynamic SQL, before directly executing, it is always a good practice to see whether the query is formed correctly using DBMS_OUTPUT. I would also suggest to use quoting string literal technique to make it even easier.
DBMS_OUTPUT.PUT_LINE(dlt_query);
The issue with the query is that you are missing the single-quotation marks around the VARCHAR2 type.
Modify the query to -
dlt_query := 'delete from '||P_TBL_NAME||' where system='||P_SYS||
' And batch_id='||''''||P_B_ID|| '''' ||
' And cobdate='||P_C_DATE;
you are losing the quotes around N1 during concatination
you can fix by adding quotes before and after , eg.
dlt_query := 'delete from '||P_TBL_NAME||' where system='||P_SYS||
' And batch_id='||''''||P_B_ID|| '''' ||
' And cobdate='||P_C_DATE;
If you have to use the EXECUTE IMMEDIATE statement, you should use bind variables:
CREATE OR REPLACE PROCEDURE prd_delete (P_TBL_NAME IN VARCHAR2,
P_SYS VARCHAR2,
P_B_ID VARCHAR2,
P_C_DATE NUMBER) IS
dlt_query VARCHAR2 (200);
BEGIN
dlt_query := 'delete from ' || P_TBL_NAME || ' where system=:1 and batch_id=:2 and cobdate=:3';
BEGIN
EXECUTE IMMEDIATE dlt_query USING P_SYS, P_B_ID, P_C_DATE;
EXCEPTION
WHEN OTHERS THEN
-- catch exception !!
END;
END;
/

concatenating 2 strings issue in Oracle

I have another issue that I do not know what has caused it. I am new to Oracle and I am tumbling on concatenating 2 strings. I have a function that is going to take a DateStart, DateStop and other variables then turn them into a SQL stmt. The function was compiling fine but has the following error when executed:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small ORA-06512 at line ...
At first I thought that one of the strings might be missing a quote, but both of the strings are fine as the function did not generate error when I printed the single one out. It had issue only when both of them are combined!
It may be something obvious that I have overlooked, I have even tried concat instead of the operator "||", but I was not able to pin point the cause of the issue. If someone has come across this issue before, I would appreciate it if you could give me a hint of what I was doing incorrectly.
This is the function that caused me issue, I have trimmed out all other irrelevant codes so that we could zero in the problem. The function compiled correctly but had issue when executed.
Thanks!
create or replace
FUNCTION ABC(
DateStart IN VARCHAR2 ,
DateStop IN VARCHAR2 ,
ZipCode IN VARCHAR2 ,
PracticeID IN VARCHAR2)
RETURN VARCHAR2
IS
v_code NUMBER;
v_errm VARCHAR2(64);
sqlstmt VARCHAR2(1000);
sqlstmt2 VARCHAR2(500);
sConditionClause VARCHAR2(500);
s_Quote VARCHAR(1) := chr(39);
BEGIN
sqlstmt2 :='SELECT TO_CHAR("Date", ''yyyy-mm-dd'') AS "Date" ,
substr(trim("Postal"),1,5) AS "ZipCode" ,
count ("Patient") AS "Total" ';
sConditionClause := ' FROM "ABC_TABLE" WHERE "Date">=To_Date('
||s_Quote || trim(DateStart) ||s_Quote
||','
||s_Quote||'mm/dd/yyyy'||s_Quote||')AND "Date"<=To_Date('
||s_Quote || trim(DateStop) ||s_Quote
||','||s_Quote||'mm/dd/yyyy'||s_Quote||') ';
sqlstmt := trim(sqlstmt2)||trim(sConditionClause);
RETURN sqlstmt;
END;
One of your variables (sqlstmt2, sConditionClause, or sqlstmt) is too small for the string you're trying to assign to it. If you had included the line number that the error is occurring on, we'd know which one.
Incidentally, you can use two single quotes together to add a single quite to a string, rather than having to use s_Quote as you are.
You could avoid this issue altogether by skipping the local variables:
create or replace
FUNCTION ABC(
DateStart IN VARCHAR2,
DateStop IN VARCHAR2)
RETURN VARCHAR2
IS
BEGIN
RETURN 'SELECT TO_CHAR("Date", ''yyyy-mm-dd'') AS "Date" ,
substr(trim("Postal"),1,5) AS "ZipCode" ,
count ("Patient") AS "Total"
FROM "ABC_TABLE" WHERE "Date">=To_Date('''
|| trim(DateStart)
||''',''mm/dd/yyyy'') AND "Date"<=To_Date('''
|| trim(DateStop)
||''',''mm/dd/yyyy'')';
END ABC;
This is what I found out: The function above may not have issue and the concatenation was fine. The issue was from the unit test Run PL/SQL provided by SQL Developer. It defaulted the return value string with a length of 200 char?!. I was not familiar enough with SQL Developer to discover it and blamed on my own function. As soon as I changed the length of the return string to varchar2(5000) everything run like a charm

Searching data from table by passing table name as a parameter in PL/SQL

Is this stored procedure in oracle is correct for searching data from table by passing table name as a parameter
CREATE OR REPLACE PROCEDURE bank_search_sp
(
p_tablename IN VARCHAR2,
p_searchname IN VARCHAR2,
p_bankcode OUT VARCHAR2,
p_bankname OUT VARCHAR2,
p_dist_code OUT NUMBER
)
AS
v_tem VARCHAR2(5000);
BEGIN
v_tem := 'SELECT bankcode,bankname,dist_code FROM ' || UPPER (p_tablename) || '
WHERE bankname LIKE '''|| p_searchname||'''';
EXECUTE IMMEDIATE v_tem
INTO p_bankcode,p_bankname,p_dist_code
USING p_searchname ;
END bank_search_sp;
If you need this procedure, then I guess that you have several tables with the columns bankcode, bankname and dist_code. If this is true, then try to normalize your model if possible.
The USING term is the correct approach, but you have to use the parameter in your query.
To avoid SQL injection, you could use dbms_assert.sql_object_name.
This should work for you:
v_tem := 'SELECT bankcode, bankname, dist_code FROM '
|| dbms_assert.sql_object_name(p_tablename)
|| ' WHERE bankname LIKE :1';
Your EXECUTE IMMEDIATE will throw an exception when finding no row or more than one row, so using LIKE might not be a good idea.
Questions that you should ask yourself:
Is the model properly normalized?
Do you really need to use LIKE, or is = what you want?
If you want to use LIKE, how should the program deal with NO_DATA_FOUND / TOO_MANY_ROWS exceptions?

Resources