Error in dynamic SQL query. Missing expression with correct syntax - sql

I have a problem working with a stored procedure on work.
It's a stored procedure which is used by several bigger procedures. The procedure which does the work is called C, and the ones who call it are A and B.
The problem I have is that when I run A, everything goes smoothly, but when I run B, I get a missing expression error, which I don't get when I call A, even tough B and A have the same flow of procedure calls (so, it's kind of a really weird error).
The C procedure has the same code:
procedure C ( LO in varchar2,
EN in varchar2,
CA in array1d,
IA in number,
AR in array2d )
is
CQ varchar2(5);
ET varchar2(2000);
OL varchar2(100);
NE varchar2(100);
TE varchar2(100);
XU varchar2(100);
begin
LO := '05';
TE := 'VAR';
CQ := '''';
OL := CQ || LO || CQ;
TE := CQ || TE || CQ;
NE := CQ || EN || CQ;
ET := 'PAR1 = ' || CA(1) || ',' ||
'PAR2 = ' || CA(2) || ',' ||
'PAR3 = ' || CA(3) || ',' ||
'PAR4 = ' || CA(4) || ',' ||
'PAR5 = ' || CA(5) || ',' ||
'PAR6 = ' || CA(6) || ',' ||
'PAR7 = ' || CA(7) || ',' ||
'PAR8 = ' || CA(8) || ',' ||
'PAR9 = ' || CA(9);
execute immediate 'update table_st set ' || ET || '
where field1 = ' || OL || '
AND field2 = ' || NE || '
AND field3 = ' || TE;
end;
The error I get is missing expression, and it seems to appear on the first line of the execute immediate. After some analysis, I realized the data from the 2d ARRAY is initialized with some empty strings ''.
I am wondering if you could help me to see this error from a new perspective, because as I see it, there seems to be no syntax error to justify the missing expression error I get, but I know I may be wrong.
If further details are needed, let me know.

At a minimum, I'd say the variables LO, CQ haven't been declared, and EN and CA haven't been declared or initialized.
Also, you declared NE as a verchar2 when I think you meant varchar2.
I also agree with Juan's observation that you may want to double check this line:
CQ := q'[']';
Did you mean:
CQ := q'['']';
Honestly not sure, but worth a look.
Oracle should tell you if the procedure compiles, and I think these errors alone will prevent it from doing that. If you don't ever declare the contents of EN and CA, it might still compile but fail when it runs.

Related

PL/SQL Cursors - Retrieving data from multiple tables

I need to input a product code as a parameter that will retrieve said product and other information. I require more info than I have provided, I've started small scale to attempt to debug any errors early, but I cannot see why the code below doesn't work. I'm using SQLPlus, any help would be greatly appreciated.
CREATE OR REPLACE PROCEDURE product_info(PRODUCT_NO IN CHAR)
AS
v_product PRODUCTS%ROWTYPE;
v_suborders SUBORDERS.ORDER_NO%TYPE;
CURSOR cur_products IS
SELECT p.name, p.prod_id, p.description, p.unit_price, s.order_no
FROM PRODUCTS P, SUBORDERS S
WHERE p.prod_id = product_no;
BEGIN
OPEN cur_products;
LOOP
FETCH cur_products INTO v_product, v_suborders;
DBMS_OUTPUT.PUT_LINE('Product Code: ' || v_product.prod_id
|| ' Name: ' || v_product.name
|| ' Description: ' || v_product.description
|| ' Price: ' || v_product.unit_price
|| ' Order: ' || v_suborders);
END LOOP;
EXCEPTION
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE ('Product number does not exist');
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE ('Operation failed ' || 'SQLCODE: ' || SQLCODE);
ROLLBACK;
END;
/
When we fetch into a variable its structure must match the projection of the query we're fetching. Or if we're fetching into multiple variables we need one variable per column in the projection. Your code doesn't do either.
The simplest solution is to use your cursor to define the variable, like this:
CURSOR cur_products IS
SELECT p.name, p.prod_id, p.description, p.unit_price, s.order_no
FROM PRODUCTS P, SUBORDERS S
WHERE p.prod_id = product_no;
v_rec cur_products%ROWTYPE;
BEGIN
OPEN cur_products;
LOOP
FETCH cur_products INTO v_rec;
...
An alternate solution is to use an implicit cursor. You could rewrite your code like this:
CREATE OR REPLACE PROCEDURE product_info(PRODUCT_NO IN CHAR)
AS
BEGIN
for v_rec in (SELECT p.name, p.prod_id, p.description, p.unit_price, s.order_no
FROM PRODUCTS P, SUBORDERS S
WHERE p.prod_id = product_no )
LOOP
DBMS_OUTPUT.PUT_LINE('Product Code: ' || v_rec.prod_id
|| ' Name: ' || v_rec.name
|| ' Description: ' || v_rec.description
|| ' Price: ' || v_rec.unit_price
|| ' Order: ' || v_rec.odrer_no);
END LOOP;
EXCEPTION
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE ('Product number does not exist');
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE ('Operation failed ' || 'SQLCODE: ' || SQLCODE);
ROLLBACK;
END;
/
Incidentally, you query doesn't have a join between PRODUCTS and SUBORDERS so your result set will be a product (cross-join) or all the records in both tables. Almost certainly you don't want that.
Also, it is bad practice not to re-raise an exception in the exception handler (apart from some edge cases). It doesn't matter in a piece of toy code like this, but don't fall into bad habits.

PL/SQL Variable parameters in procedure

I need to create a procedure that allows a user to input information for an order (which is below), but also for n number of suborders (where 2 ≤ n ≥ 10). How would I be able to incorporate this into the procedure I already have? I'm using SQLPlus. Any help or advice would be greatly appreciated. :)
CREATE OR REPLACE PROCEDURE VIEW_ORDER(ORDERS IN CHAR)
AS
CURSOR ORDER_CUR IS
SELECT * FROM SUBORDERS
WHERE ORDER_NO = ORDERS;
BEGIN
FOR O_REC IN ORDER_CUR
LOOP
DBMS_OUTPUT.PUT_LINE('Product Code: ' || O_REC.prod_id
|| ' Order: ' || O_REC.order_no
|| ' Suborder: ' || O_REC.suborder_no
|| ' Quantity: ' || O_REC.quantity);
END LOOP;
EXCEPTION
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE ('Product number does not exist');
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE ('Operation failed ' || 'SQLCODE: ' || SQLCODE);
ROLLBACK;
END;
/
You can use for an input sqlplus command ACCEPT and make another loop to take n suborders.
If I didn't missunderstood your question it would be something like this:
-- You have to create the input outside the procedure
accept n char prompt 'Please enter n suborders: '
CREATE OR REPLACE PROCEDURE VIEW_ORDER(ORDERS IN CHAR)
AS
CURSOR ORDER_CUR IS
SELECT * FROM SUBORDERS
WHERE ORDER_NO = ORDERS;
BEGIN
FOR O_REC IN ORDER_CUR
LOOP
-- Here we check if n<=2
IF n>=2 THEN
-- here if n<=10
WHILE n<=10
LOOP
DBMS_OUTPUT.PUT_LINE('Product Code: ' || O_REC.prod_id
|| ' Order: ' || O_REC.order_no
|| ' Suborder: ' || O_REC.suborder_no
|| ' Quantity: ' || O_REC.quantity);
END LOOP;
END IF;
END LOOP;
EXCEPTION
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE ('Product number does not exist');
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE ('Operation failed ' || 'SQLCODE: '
||
SQLCODE);
ROLLBACK;
END;
/

Not properly closed sql command

I have this code:
def_where:=def_where||' TO_CHAR(date_of_input,''MM'') like '''||'to_char(date_of_input,''MM'')=nvl(:DSP_month,to_char(date_of_input,''MM''))'||'%' ||'to_char(date_of_input,''RRRR'')=nvl(:DSP_year,to_char(date_of_input,''RRRR''))'||'%''';
and i m getting error sql command not properly ended.
def_where suggest, that this is part of dynamically built query, condition of where clause. But produced string makes no sense, it should be something like:
declare
def_where varchar2(32767) := '';
begin
def_where := def_where
|| ' to_char(date_of_input, ''MM'') '
|| ' = nvl(:DSP_month, to_char(date_of_input, ''MM'')) and'
|| ' to_char(date_of_input, ''RRRR'') '
|| ' = nvl(:DSP_year, to_char(date_of_input, ''RRRR''))';
dbms_output.put_line(def_where);
end;
Look what you got from dbms_output, correct this syntax if needed. You could also rewrite your string to get this:
(extract(month from date_of_input) = :DSP_month or :DSP_month is null) and
(extract(year from date_of_input) = :DSP_year or :DSP_year is null)

ORA-00933 Error for to_char formatting PLSQL

I have the following codes:
IF nvl(p_value, 0) >= 0 THEN
l_currency_prefix := 'scc.currency_prefix_pos';
l_currency_suffix := 'scc.currency_suffix_pos';
ELSE
l_currency_prefix := 'scc.currency_prefix_neg';
l_currency_suffix := 'scc.currency_suffix_neg';
END IF;
l_query := 'SELECT nvl('||l_currency_prefix||', '')'
||'trim(to_char('||p_value||
',scc.currency_format
,'||'NLS_NUMERIC_CHARACTERS=' || 'scc.decimal_group_separator'||'))'
||'nvl('||l_currency_suffix||', '')
FROM gss.gss_currency_locale scc
WHERE scc.country_code =' ||p_country_code||
'AND scc.currency_code ='|| p_currency_code||
'AND rownum=1';
and here is the dbms output for l_query:
SELECT nvl(scc.currency_prefix_pos, ')trim(to_char(10000,scc.currency_format
,NLS_NUMERIC_CHARACTERS=scc.decimal_group_separator))nvl(scc.currency_suffix_pos, ')
FROM gss.gss_currency_locale scc
WHERE scc.country_code =USAND scc.currency_code =USDAND rownum=1
However, it keep showing an ORA-00933 errors.
I debug these piece of code for few hours and could not find where is the errors.
Could some one please provide some advice on this?
Now some of the problems are obvious. You need something like this:
l_query := 'SELECT nvl('||l_currency_prefix||',
||'trim(to_char('||p_value||
',scc.currency_format || ')' ||
FROM gss.gss_currency_locale scc
WHERE scc.country_code = ''' ||p_country_code|| '''' ||
' AND scc.currency_code = '''|| p_currency_code|| '''' ||
' AND rownum=1';
(I'm not sure if that is 100 percent correct.)
Usually, when creating queries this way, I use replace() instead of direct substitution. Something like:
l_query := 'select nvl(#currency_prefix, trim(#p_value, #currency_format))
from . . . ';
l_query := replace(l_query, '#currency_prefix', l_currency_prefix);
l_query := replace(l_query, '#p_value', p_value);
. . .
I find that this approach makes it much easier to maintain the code and to see what it is doing.

SQL stmt concatenate(sp*)

I’m dong some SQL work this week and I normally write Java.
I need to add some if's into my SQL like so, but don’t want to do any string concatenation.
This should be an easy one: I just don’t know the SQL syntax
I want to make all those "AND" statements "if" conditions for params I’m passing in
Would I have to do something like this:
IF p_sac IS NOT NULL
THEN
stmt := stmt || ' AND nsns.sac = ''' || p_sac || '''';
END IF;
IF p_value1 IS NOT NULL
THEN
stmt := stmt || ' AND UPPER(value1s.value1) LIKE ''' || UPPER(p_value1) || ''' ';
END IF;
Or is there an alternative to this above?
Basically I've got this:
FUNCTION summarize_item_search_data (p_obj_code IN VARCHAR2, p_value1 IN VARCHAR2,
p_sac IN NUMBER, p_job_type_id IN NUMBER,
p_value4 IN NUMBER)
RETURN sys_refcurvalue2
IS
result_cur sys_refcurvalue2;
BEGIN
OPEN result_cur FOR
SELECT DISTINCT jp.id, jp.row_top.mwslin AS mwslin, jp.obj_code, jp.jobload_year, jp.row_top.fiscal_year AS fiscal_year,
nsns.sac, value1s.value1, nsns.nsn,
DECODE( jp.row_top.nsn_id, NULL, jp.row_top.nomenclature ,nsns.nomenclature) AS nomenclature, jp.row_top.value4 AS value4
FROM scabs sch, jobs JP, master_nsn nsns, master_value1 value1s, TABLE(value1s.group_id) (+) ntab,
groups pgds
WHERE jp.row_top.nsn_id = nsns.id(+) AND nsns.value1_id = value1s.id(+)
-- stmt := stmt || ' AND ''' || p_year || ''' = ntab.fiscal_year(+)';
AND ntab.group_id = pgds.id(+)
AND nsns.sac = p_sac
AND UPPER(value1s.value1) LIKE UPPER(p_value1)
AND UPPER(jp.obj_code) = UPPER(p_obj_code)
AND jp.row_top.value4 <= p_value4
AND jp.row_top.job_type_id =p_job_type_id
RETURN result_cur;
END summarize_item_search_data;
Just put it all into your WHERE clause as conditions:
WHERE
(p_sac IS NULL OR nsns.sac = p_sac) AND
....
If performance suffers greatly then you might want to look into using dynamic SQL, but I would start with the approach above.