Oracle SQL: Find {tags} in a sql string - sql

I have SQL strings that my users write. They look like:
SELECT Name, Age from Users WHERE Name LIKE '%a%' AND {UsersWhere}
On the oracle server side when such an SQL is to be executed I want to replace the {tags} first. The replacements for the {tags} will be valid SQL sub strings I am holding in a table. Pre-manufactered sub sqls. So the treated string will be valid SQL.
Is there some fancy build-in Oracle function to make this happen?
Thanks for a hint!

I have written a small function for anyone interested:
CREATE OR REPLACE FUNCTION SA.REPLACE_VARIABLES (p_sql IN VARCHAR2)
RETURN VARCHAR2
IS
vs_return VARCHAR2 (4000);
-- Deklarationen
vs_sql VARCHAR2(4000);
vs_substring VARCHAR2(4000);
vs_variable VARCHAR2(200);
vs_variable_content VARCHAR2(4000);
BEGIN
vs_sql := p_sql;
IF INSTR(p_sql, '{') > 0 THEN
vs_substring := vs_sql;
WHILE LENGTH(vs_substring) > 0 LOOP
IF INSTR(vs_substring, '{') <> 0
THEN
vs_variable := SUBSTR(vs_substring, INSTR(vs_substring, '{'), INSTR(vs_substring, '}') - INSTR(vs_substring, '{') + 1);
-- Do whatever you want with the variable
--vs_sql := REPLACE(vs_sql, vs_variable, vs_variable_content);
-- Substring verkürzen
vs_substring := SUBSTR(vs_substring, INSTR(vs_substring, vs_variable) + LENGTH(vs_variable) + 1);
ELSE
vs_substring := '';
END IF;
END LOOP;
END IF;
RETURN vs_sql;
EXCEPTION
WHEN OTHERS
THEN
-- Err -handle
END REPLACE_VARIABLES;
/

I'd just keep it simple:
v_sql := REPLACE(v_sql, '{UsersWhere}', '...whatever you need...');

Related

PLS-00103: Encountered the symbol "=" when expecting one of the following.... in PL/SQL script

create or replace function lstnation (listdisplay in varchar2)
return varchar2 is
nName varchar2 (1000) default null;
listD varchar2(1000) default null;
cursor display_nation
is
select nation.n_name
from nation
inner join region
on region.r_regionkey = nation.n_nationkey
where region.r_regionname = listdisplay;
BEGIN
open display_nation;
loop
fetch display_nation into nName;
exit when display_nation%notfound;
IF
listD := listD || RTRIM(nName)||' , ';
end loop;
close display_nation;
return listD;
end lstnation;
/
DECLARE
rKey region.r_regionkey%type;
rName region.r_name%type;
nList varchar2(1000);
cursor outer_block is
select region.r_regionkey, region.r_name, lstnation(region.r_name)
from region;
BEGIN
open outer_block;
loop
fetch outer_block into rKey, rName, nList;
exit when outer_block%notfound;
dbms.output.put_line(rkey || ' ' || RTRIM(rName) || ': '|| nList);
end loop;
close outer_block;
end;
/
I get two errors, how can I fix it
LINE/COL ERROR
19/12 PLS-00103: Encountered the symbol "=" when expecting one of the
following:
. ( * # % & = - + < / > at in is mod remainder not rem then
<an exponent (**)> <> or != or ~= >= <= <> and or like like2
like4 likec between || multiset member submultiset
20/2 PLS-00103: Encountered the symbol "END" when expecting one of the
following:
begin function pragma procedure subtype type
current cursor delete
exists prior
You can save some coding and efficiency by replacing the cursor loop with the listagg function
select listagg(rtrim(nation.n_name),',')
from nation
inner join region
on region.r_regionkey = nation.n_nationkey
where region.r_regionname = listdisplay;
So that will collate all the matching rows, and use whatever delimiter is passed in. One thing to be aware of, you have listD varchar2(1000) so as long as the results from the query are less than 1000, you are OK. If you expect a larger result set, you may need to increase or use a clob.
If for some reason, you still want to use the loop method, then you need to fix your IF statement:
loop
fetch display_nation into nName;
exit when display_nation%notfound;
IF <condition> THEN
listD := listD || RTRIM(nName)||' , ';
END IF;
end loop;

adding numbers to same index in associative arrays plsql

I'm trying to add a few consecutive numbers to the same index in an associative array in PL/SQL (Oracle).
I have defined the associative array as follows:
TYPE map_varchar IS TABLE OF NUMBER(30) INDEX BY VARCHAR2(30);
l map_varchar;
I have an XML clob which I loop through and get a description(varchar) and amount(number) values. Say I have a description: 'A' and the following value 3,5,6
I want to map this as: 'A' > 14
for r in (SELECT t.*
FROM XMLTABLE('*'
PASSING xmltype(rec1.xml).
EXTRACT('/ProductS/')
COLUMNS description VARCHAR2(30) PATH 'some_xml_tag',
amount NUMBER(30) PATH '_another_xml_tag') t)
LOOP
l(r.description) := l(r.description) + r.amount;
dbms_output.put_line('v_modifier_value_p: ' || r.description || ' amount: ' || r.saving);
END LOOP;
I'm trying the above but the line:
l(r.description) := l(r.description) + r.amount;
is not working.
How can this be done in PLSQL?
l(r.description) apparently needs to be initialized. So I added something like this:
if l.exists(r.description) then
null;
else
l(r.description) := 0;
end if;
this works now.
replace the statement
l(r.description) := l(r.description) + r.amount;
with the following
if l.exists(r.description) then
l(r.description) := l(r.description) + r.amount;
else
l(r.description) := r.amount;
end if;

PLS-00306: when calling a function from a PL/SQL block

I am getting the PLS-00306 error when I attempt to run a PL/SQL block that calls a variety of things including a function. The job of the function is to count how many cars belong to a certain model type. The function works if I call it in a SQL statement or it's own block, it just doesn't seem to work here.
This is the function:
CREATE OR REPLACE Function findtotalcarmodels(
model_name_in IN varchar2)
RETURN NUMBER
IS
counter NUMBER := 0;
CURSOR car_count_cur IS
SELECT model_name
FROM i_car
WHERE model_name = model_name_in;
Rec_car_details car_count_cur%ROWTYPE;
BEGIN
OPEN car_count_cur;
LOOP
FETCH car_count_cur INTO Rec_car_details;
EXIT WHEN car_count_cur%NOTFOUND;
counter := counter + 1;
END LOOP;
CLOSE car_count_cur;
RETURN counter;
END;
This is the Block:
SET SERVEROUTPUT ON FORMAT WRAP SIZE 12000
Declare
v_model VARCHAR2(40);
v_carcategory VARCHAR2(40);
v_totalcars NUMBER;
v_maxdate DATE:=TO_DATE(1, 'J');
Cursor carcur IS
SELECT *
FROM i_car;
CURSOR c1(v_car_registration VARCHAR2) IS
SELECT *
from i_booking a
WHERE a.registration=v_car_registration;
Begin
For car_rec in carcur
LOOP
v_maxdate:=TO_DATE(1, 'J');
for rec in c1(car_rec.registration)
loop
IF rec.date_reserved > v_maxdate
then
v_maxdate:=rec.date_reserved ;
If car_rec.Cost <=50000
THEN
v_carcategory := 'Budget Car';
End IF;
If car_rec.Cost BETWEEN 50000 AND 100000
THEN
v_carcategory := 'Standard Car';
End IF;
If car_rec.Cost >100000
THEN
v_carcategory := 'Premium Car';
End If;
end IF;
v_totalcars := findtotalcarmodels;
end loop;
DBMS_OUTPUT.PUT_LINE('Registration:'|| ' '|| car_rec.registration);
DBMS_OUTPUT.PUT_LINE('Cost:'|| ' $' || car_rec.Cost);
DBMS_OUTPUT.PUT_LINE('Model Name:'|| ' '|| car_rec.model_name);
DBMS_OUTPUT.PUT_LINE('Car Category:'|| ' '||v_carcategory);
DBMS_OUTPUT.PUT_LINE('Total number of Cars:'|| ' '||v_totalcars);
DBMS_OUTPUT.PUT_LINE('Most Recent Rental Date: '|| ' '||v_maxdate);
DBMS_OUTPUT.NEW_LINE;
END LOOP;
END;
/
Before I get slammed for the style of the PL/SQL block, just keep in mind that it is written to requirement and everything works well with the exception of the function.
If someone could point me in the right direction to call this function without error I would be very grateful.
It looks like you have missed to pass the IN parameter to the function.
Try like this,
v_totalcars := findtotalcarmodels('<model_name_in>');
Well, when i see things right, you don't fillup the parameter model_name. When you don't have an overriden function wihtout parameter you need to fill it up.
AS you can also see the PLS-00306 is telling you something about wrong number of arguments.

how can we get the value of of VARRAY from IN Parameter in procedure

I am new to PL/SQL... In Varray how can i get multiple value from IN parameter..... else Is there another ways to get the values...
I want to interate the values through VArray... if any other options then its fine..
coding:
CREATE OR REPLACE
PROCEDURE dynamic_query_build(
vr_plan_sku_id IN VARCHAR2 )
IS
type plan_sku_id_array IS VARRAY(999) OF VARCHAR2(5000);
plan_sku_id plan_sku_id_array;
total INTEGER;
vrx_plan_sku_id VARCHAR2(3000);
BEGIN
vrx_plan_sku_id:= REPLACE(vr_plan_sku_id,',',chr(39)||','||chr(39));
plan_sku_id := plan_sku_id_array(chr(39)||vrx_plan_sku_id||chr(39));
total := plan_sku_id.count;
FOR i IN 1 .. total
LOOP
dbms_output.put_line(plan_sku_id(i));
END LOOP;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END dynamic_query_build;
Execution:
set serveroutput on;
declare
vr_plan_sku_id varchar2(200) := '5863314,5863315';
BEGIN
dynamic_query_build(vr_plan_sku_id);
END;
/
My Output:
anonymous block completed
'5863314','5863315'
Expected output:
5863314
5863315
now it is considering as single value....
I created anonymous block with procedure dynamic_query_build. Added there code, that will split VARCHAR2 variable into varray.
I think, the key to your question is this line - plan_sku_id.EXTEND();
You can extend varray dynamically, but only till it reaches defined maximum (in your case - 999).
DECLARE
vr_plan_sku_id varchar2(200) := '5863314,5863315';
PROCEDURE dynamic_query_build(
vr_plan_sku_id IN VARCHAR2 )
IS
type plan_sku_id_array IS VARRAY(999) OF VARCHAR2(5000);
plan_sku_id plan_sku_id_array;
total INTEGER;
position PLS_INTEGER := 0;
last_position PLS_INTEGER := 1;
tmp VARCHAR2(5000);
counter PLS_INTEGER := 1;
BEGIN
plan_sku_id := plan_sku_id_array();
LOOP
position := INSTR(vr_plan_sku_id, ',', last_position);
IF position > 0 THEN
tmp := SUBSTR(vr_plan_sku_id, last_position, position - last_position);
last_position := position + 1;
ELSE
tmp := SUBSTR(vr_plan_sku_id, last_position);
END IF;
plan_sku_id.EXTEND();
plan_sku_id(counter) := tmp;
counter := counter + 1;
EXIT WHEN position = 0 OR counter > 10;
END LOOP;
total := plan_sku_id.count;
FOR i IN 1 .. total
LOOP
dbms_output.put_line(plan_sku_id(i));
END LOOP;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END dynamic_query_build;
BEGIN
dynamic_query_build(vr_plan_sku_id);
END;
/
Put a replace in the dbms_output statement this will eliminate the quotes from the string
....
dbms_output.put_line replace (replace (plan_sku_id(i), '''' ))',',chr(10);
.....

Oracle : how to fetch data from dynamic query?

I have a program to generate dynamic query string based on input. This query may select from any tables or joined tables in my DB, and the column names and number of columns are unknown.
Now with this query string as the only input, I want to fetch all data from the result and output them line by line, is there any way to do this ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Thank Thinkjet for the reference. I have solved the problem, to help the others, here is the piece of code I used:
DECLARE
v_curid NUMBER;
v_desctab DBMS_SQL.DESC_TAB;
v_colcnt NUMBER;
v_name_var VARCHAR2(10000);
v_num_var NUMBER;
v_date_var DATE;
v_row_num NUMBER;
p_sql_stmt VARCHAR2(1000);
BEGIN
v_curid := DBMS_SQL.OPEN_CURSOR;
p_sql_stmt :='SELECT * FROM emp';
DBMS_SQL.PARSE(v_curid, p_sql_stmt, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS(v_curid, v_colcnt, v_desctab);
-- Define columns:
FOR i IN 1 .. v_colcnt LOOP
IF v_desctab(i).col_type = 2 THEN
DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_num_var);
ELSIF v_desctab(i).col_type = 12 THEN
DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_date_var);
ELSE
DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_name_var, 50);
END IF;
END LOOP;
v_row_num := dbms_sql.execute(v_curid);
-- Fetch rows with DBMS_SQL package:
WHILE DBMS_SQL.FETCH_ROWS(v_curid) > 0 LOOP
FOR i IN 1 .. v_colcnt LOOP
IF (v_desctab(i).col_type = 1) THEN
DBMS_SQL.COLUMN_VALUE(v_curid, i, v_name_var);
ELSIF (v_desctab(i).col_type = 2) THEN
DBMS_SQL.COLUMN_VALUE(v_curid, i, v_num_var);
ELSIF (v_desctab(i).col_type = 12) THEN
DBMS_SQL.COLUMN_VALUE(v_curid, i, v_date_var);
END IF;
END LOOP;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(v_curid);
END;
/
You can do that with DBMS_SQL package.
Update
To get more detailed reference about DBMS_SQL go here.
If you are building your string within PL/SQL, you can run it with EXECUTE IMMEDIATE. <- link. Use the BULK COLLECT INTO and output the collection.
<PRE>
DECLARE
RUN_S CLOB;
IGNORE NUMBER;
SOURCE_CURSOR NUMBER;
PWFIELD_COUNT NUMBER DEFAULT 0;
L_DESCTBL DBMS_SQL.DESC_TAB2;
Z_NUMBER NUMBER;
BEGIN
RUN_S := ' SELECT 1 AS VAL1,
2 AS VAL2,
CURSOR (SELECT 11 AS VAL11,
12 AS VAL12
FROM DUAL) AS CUR1,
CURSOR (SELECT 11 AS VAL11,
12 AS VAL12
FROM DUAL) AS CUR2
FROM DUAL';
SOURCE_CURSOR := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(SOURCE_CURSOR, RUN_S, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS2(SOURCE_CURSOR, PWFIELD_COUNT, L_DESCTBL); -- get record structure
FOR I IN 1 .. PWFIELD_COUNT LOOP
DBMS_OUTPUT.PUT_LINE('Col ' || I || ' Type:' || L_DESCTBL(I).COL_TYPE);
IF L_DESCTBL(I).COL_TYPE = 2 THEN
DBMS_SQL.DEFINE_COLUMN(SOURCE_CURSOR, I, Z_NUMBER);
END IF;
NULL;
END LOOP;
IGNORE := DBMS_SQL.EXECUTE(SOURCE_CURSOR);
LOOP
IF DBMS_SQL.FETCH_ROWS(SOURCE_CURSOR) > 0 THEN
FOR I IN 1 .. PWFIELD_COUNT LOOP
IF L_DESCTBL(I).COL_TYPE IN (2) THEN
DBMS_SQL.COLUMN_VALUE(SOURCE_CURSOR, I, Z_NUMBER);
DBMS_OUTPUT.PUT_LINE('Col ' || I || ' Value:' || Z_NUMBER);
END IF;
END LOOP;
ELSE
EXIT;
END IF;
END LOOP;
END;
</PRE>