Issue while running a query within a procedure - oracle

I have a procedure that expects a SELECT statement as a parameter.
If I run the SELECT Statement out of the procedure, it runs fine, but when I run it within the procedure i get the error:
The procedure :
data_dump(query_in => 'select * from reservation where type not in ('H','PURGE','E') ',
file_in => 'export_'||months_from||''||months_to||'.csv',
directory_in => 'C:\Users\Administrator\Desktop\test',
delimiter_in => '|' );
The query itself gives me the expected result.
select * from reservation where type not in ('H','PURGE','E', '|| other ||')
The error is: encountered the symbol 'H'.
Can it be because of the single quotes around the IN condition?
getting error missing right parenthesis:
(trunc(a.update_date) between (select add_months(TRUNC(SYSDATE), -(]'|| months_from ||q'[') ) from dual) and (select add_months(TRUNC(SYSDATE), -(]'|| months_to ||q'[') from dual))]'

You're passing quoted arguments within a string. You need to either escape those internal quotes:
data_dump(query_in => 'select * from reservation where type not in (''H'',''PURGE'',''E'') ',
file_in => 'export_'||months_from||''||months_to||'.csv',
directory_in => 'C:\Users\Administrator\Desktop\test',
delimiter_in => '|' );
Or use the alternative quoting mechanism:
data_dump(query_in => q'[select * from reservation where type not in ('H','PURGE','E') ]',
...
With your 'other' value you need to still include that in its own escape quotes:
data_dump(query_in => 'select * from reservation where type not in (''H'',''PURGE'',''E'','''|| other ||''')',
...
or (getting less readable now, perhaps):
data_dump(query_in => q'[select * from reservation where type not in ('H','PURGE','E',']' || other || q'[')]',
...
You can use dbms_output to debug this sort of thing, printing the generated argument and seeing if that matches what you've been running manually.
With your third partial code you've put closing single quotes after the (presumably numeric) variables:
(trunc(a.update_date) between (select add_months(TRUNC(SYSDATE), -(]'|| months_from ||q'[') ) from dual) and (select add_months(TRUNC(SYSDATE), -(]'|| months_to ||q'[') from dual))]'
^ ^
and you are, as the message says, missing a closing parenthesis in the second add_month call; so it should be (where ... is the rest of the statement that you haven't shown):
q'[ ... (trunc(a.update_date) between (select add_months(TRUNC(SYSDATE), -(]'
|| months_from ||q'[) ) from dual) and (select add_months(TRUNC(SYSDATE), -(]'
|| months_to ||q'[) ) from dual))]'
Again, this is basic debugging you can do by printing the generated statement and inspecting it or trying to run it manually.
Also you don't need the inner select from dual stuff or all the parentheses; you can just do:
q'[ ... trunc(a.update_date) between add_months(TRUNC(SYSDATE), -]'
|| months_from ||q'[) and add_months(TRUNC(SYSDATE), -]'|| months_to ||q'[)]'

Related

In a CASE statement, can you store WHEN subquery result for use in the THEN output?

I have a report outputting the results of a query which was designed to provide links to a webpage:
SELECT
a,
b,
c,
'string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%URL_LINK~' || ipp_code || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY
) AS URL
FROM men_ipp
This works well, but I was asked to amend it so that if the records needed to generate the URL were missing (ie. sso_code can't be retrieved), it outputs a warning message instead of the subquery output.
Since there's always going to be a string of a set length (6 characters in this example), my solution was to create a CASE statement which is evaluating the length of the subquery output, and if the answer is greater than 6 characters it returns subquery result itself, otherwise it returns a warning message to the user. This looks like:
SELECT
a,
b,
c,
CASE
WHEN
LENGTH('string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || (ipp_code) || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY)
) > 6
THEN
('string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || (ipp_code) || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY)
ELSE 'warning message'
END AS URL
FROM men_ipp
The statment works fine, however the processing time is nearly doubled because it's having to process the subquery twice. I want to know if there's any way to store the result of the subquery in the WHEN, so it doesn't need to be run a second time in the THEN? eg. as a temporary variable or similar?
I've tried to declare a variable like this:
DECLARE URLLINK NVARCHAR(124);
SET URLLINK = 'string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%URL_LINK~' || ipp_code || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY
)
However this causes the query to error saying the it Encountered the symbol "https://evision.dev.uwl.tribalsits.com/urd/sits.urd/run/siw_file_load.sso?" when expecting one of the following: := . ( # % ; not null range default character
You can use NULLIF to make the result null if it is "string" (i.e., you appended nothing to it from your subquery). Then use NVL to convert to the warning message. Something like this:
SELECT
a,
b,
c,
nvl(nullif(
'string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || (ipp_code) || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY),'string'),'warning message')
FROM men_ipp
Use a CTE.
with temp as
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || (ipp_code) || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY
)
select a, b, c,
case when sso_code is null then 'warning message'
else 'string' || sso_code
end as url
from men_ipp full outer join temp on 1 = 1;
Use a sub-query:
SELECT a,
b,
c,
CASE
WHEN LENGTH(sso_code) > 6
THEN sso_code
ELSE 'warning message'
END AS URL
FROM (
SELECT a,
b,
c,
'string' ||
( SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || ipp_code || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY ) AS sso_code
FROM men_ipp
)

varchar in execute immediate

I'm using the following script pl/sql:
EXECUTE IMMEDIATE 'insert into TAB1(ID, CODE, TYPE, ORDRE)
select ''KEY'', ''KEY_LIB'', ''TYP_KEY'', 3 FROM dual
where not exists(
select ID,CODE,TYPE,ORDRE
FROM TAB1
where TYPE=''TYP_KEY''
AND CODE =''KEY_LIB''
)';
And I'm getting the following error :
00000 - "FROM keyword not found where expected
The error seems to be in the second line but I can't figure out what's wrong.
Can anyone help please ?
Type is a keyword in Oracle. So, try with tablename.Type when type is a column name. Here is your revised query:
EXECUTE IMMEDIATE 'insert into TAB1(ID, CODE, TYPE, ORDRE)
select ''KEY'', ''KEY_LIB'', ''TYP_KEY'', 3 FROM dual
where not exists(
select ID,CODE,TYPE,ORDRE
FROM TAB1
where TAB1.TYPE=''TYP_KEY''
AND CODE =''KEY_LIB''
)';
You should better try it like this:
EXECUTE IMMEDIATE 'insert into TAB1(ID, CODE, "TYPE", ORDRE)
select :KEY, :KEY_LIB, :TYP_KEY, 3 FROM dual
where not exists(
select ID,CODE,"TYPE",ORDRE
FROM TAB1
where "TYPE" = :TYP_KEY
AND CODE = :KEY_LIB
)'
USING 'KEY', 'KEY_LIB', 'TYP_KEY', 'TYP_KEY', 'KEY_LIB';
You can use Alternative Quoting Mechanism (''Q'') for String Literals, then you don't need double quotes. With this technique it is much easier to test queries manually (if it is needed). Btw, your query works fine.
BEGIN
EXECUTE IMMEDIATE
q'[INSERT INTO tab1(id, code, type, ordre)
SELECT 'KEY', 'KEY_LIB', 'TYP_KEY', 3
FROM dual
WHERE NOT EXISTS(
SELECT 1
FROM tab1
where tab1.type = 'TYP_KEY'
AND code = 'KEY_LIB'
)]';
END;
/

Adding alias name is giving error in oracle

I want to give alias name to my column name in a stored procedure But its not coming as I want. Below is the code which I tried
STREX:='SELECT DISTINCT AM.APP_MST_ID, NVL(AM.APPLICATIONNAME,''-'') as ''APPLICATION NAME'', NVL(AD.URLPATH,''-'')URL, NVL(AM.PROJECTNO,''-'')PROJECTNO, NVL(AM.VSS_FOLDER_LOC,''-'')VSSFOLDERLOC,
NVL(AU.NAME, ''-'')SPOCUSER, NVL(AUR.NAME,''-'')REQUESTEDBY, NVL(AUD.NAME,''-'')DELIVERYMANAGER
FROM APPLICATION_MASTER AM
INNER JOIN APPLICATION_DETAILS AD
ON AM.APP_MST_ID = AD.APP_MST_ID
INNER JOIN APPUSER_UMS AU
ON AM.APP_MST_ID = AU.APP_USERID
INNER JOIN APPUSER_UMS AUR
ON AUR.APP_USERID = AM.REQUESTED_BY_APPUSRID
INNER JOIN APPUSER_UMS AUD
ON AUD.APP_USERID = AM.DELIVERY_MANAGER_APPUSRID
WHERE UPPER(AM.'|| P_PARAM_TYPE || ') ' || P_OPERATOR || ' :PARAM';
/* WHERE AM.'|| UPPER(P_PARAM_TYPE) ||' '|| P_OPERATOR || ' :PARAM'; */
DBMS_OUTPUT.PUT_LINE('STREX '|| STREX);
OPEN P_RETURN FOR STREX USING VAL;
I want to show as Application Name for first column
Try:
...SELECT DISTINCT AM.APP_MST_ID, NVL(AM.APPLICATIONNAME,''-'') as "Application Name"...
Double quotes (") are used to specify identifier names, single quotes (') are used to delimit character strings in data.

Split owner and object name in Oracle

Given an object identified by the form owner.tablename; how do I split the owner and table name up?
Both my ideas of either string tokenization or select owner, object_name from all_objects where owner || '.' || object_name = 'SCHEMA.TABLENAME' seem like hacks.
You can use DBMS_UTILITY.name_tokenize for this purpose.
This procedure calls the parser to parse the given name as "a [. b [.
c ]][# dblink ]". It strips double quotes, or converts to uppercase if
there are no quotes. It ignores comments of all sorts, and does no
semantic analysis. Missing values are left as NULL.
e.g.
DBMS_UTILITY.NAME_TOKENIZE
( name => 'SCHEMA.TABLENAME'
, a => v_schema
, b => v_object_name
, c => v_subobject -- ignore
, dblink => v_dblink
, nextpos => v_nextpos -- ignore
);
http://docs.oracle.com/cd/E11882_01/appdev.112/e40758/d_util.htm#BJEFIFBJ
SELECT SUBSTR('SCHEMA.TABLENAME', 0, INSTR('SCHEMA.TABLENAME', '.') - 1) OWNER,
SUBSTR('SCHEMA.TABLENAME', INSTR('SCHEMA.TABLENAME', '.') + 1) TABLE_NAME
FROM DUAL

Binding variables in dynamic PL/SQL

I have a dynamic PL/SQL that will construct the SELECT statement based on what the searching criteria input from the users,likes:
l_sql := 'SELECT * INTO FROM TABLEA WHERE 1=1 ';
IF in_param1 IS NOT NULL THEN
l_sql := l_sql || 'AND column1 = in_param1 ';
END IF;
IF in_param2 IS NOT NULL THEN
l_sql := l_sql || 'AND column2 = in_param2 ';
END IF;
...................................
IF in_paramXX IS NOT NULL THEN
l_sql := l_sql || 'AND columnXX = in_paramXX ';
END IF;
To reduce the hard parse overhead , I consider to use the binding variables. However , it is difficult to manage when supplying the actual values to the binding variables as there are so many binding variables and combination of the generated SELECT statement . I cannot use the method of DBMS_SESSION.set_context() introduced at http://www.dba-oracle.com/plsql/t_plsql_dynamic_binds.htm because my account has no right to use this package. Besides , I want the generated SQL only contains the conditions on the fields that the user did not leave empty. So I cannot change the dynamic SQL to something likes
SELECT * INTO FROM TABLEA WHERE 1=1
and ( in_param1 is NULL or column1 = in_param1)
and ( in_param2 is NULL or column2 = in_param2)
...............................................
and ( in_paramXX is NULL or columnXX = in_paramXX)
So , I want to try to use the DBMS_SQL method .Can anyone give an example about how to use DBMS_SQL to call dynamic SQL with binding variables? Especially , how can I get the result executed from DBMS_SQL.execute() to the SYS_REFCURSOR , something like :
open refcursor for select .... from
The oracle version that I use is 10g and it seems that the oracle 10g does not have DBMS_Sql.To_Refcursor()
In your Oracle version you can apply some tricks to your query in order to do this. The idea is to use a query in the following form:
select *
from
(select
:possibleParam1 as param1
-- do the same for every possible param in your query
:possibleParamN as paramN
from dual
where rownum > 0) params
inner join
-- join your tables here
on
-- concatenate your filters here
where
-- fixed conditions
then execute it with:
open c for query using param1, ..., paramN;
It works by using DUAL to generate a fake row with every single param, then inner joining this fake row to your real query (without any filters) using only the filters you want to apply. This way, you have a fixed list of bind variables in the SELECT list of the params subquery, but can control which filters are applied by modifying the join condition between params and your real query.
So, if you have something like, say:
create table people (
first_name varchar2(20)
last_name varchar2(20)
);
you can construct the following query if you just want to filter on first name
select *
from
(select
:first_name as first_name,
:last_name as last_name
from dual
where rownum > 0) params
inner join
people
on
people.first_name = params.first_name;
and this if you want to filter on both first_name and last_name
select *
from
(select
:first_name as first_name,
:last_name as last_name
from dual
where rownum > 0) params
inner join
people
on
people.first_name = params.first_name and
people.last_name = params.last_name;
and in every case you would execute with
open c for query using filterFirstName, filterLastName;
It is important for performance to use the where rownum > 0 with DUAL as it forces Oracle to "materialize" the subquery. This usually makes DUAL stop interfering with the rest of the query. Anyway, you should check the execution plans to be sure Oracle is not doing anything wrong.
In 10g a DBMS_SQL cursor can't be changed into a Ref Cursor. Going through a result set through DBMS_SQL is tortuous since, as well as looping through the rows, you also have to loop through the columns in a row.
I want the generated SQL only contains
the conditions on the fields that the
user did not leave empty
Is that purely for performance reasons ? If so, I suggest you work out what the practical execution plans are and use separate queries for them.
For example, say I'm searching on people and the parameters are first_name, last_name. gender, date_of_birth. The table has indexes on (last_name,first_name) and (date_of_birth), so I only want to allow a query if it specifies either last_name or date_of_birth.
IF :p_firstname IS NOT NULL and :p_lastname IS NOT NULL THEN
OPEN cur FOR
'SELECT * FROM PEOPLE WHERE last_name=:a AND first_name=:b AND
(date_of_birth = :c or :c is NULL) AND (gender = :d or :d IS NULL)' USING ....
ELSIF :p_lastname IS NOT NULL THEN
OPEN cur FOR
'SELECT * FROM PEOPLE WHERE last_name=:a AND
(date_of_birth = :c or :c is NULL) AND (gender = :d or :d IS NULL)' USING ....
ELSIF :p_dateofbirth IS NOT NULL THEN
OPEN cur FOR
'SELECT * FROM PEOPLE WHERE date_of_birth=:a AND
(first_name=:b OR :b IS NULL) AND (gender = :d or :d IS NULL)' USING ....
ELSE
RAISE_APPLICATION_ERROR(-20001,'Last Name or Date of Birth MUST be supplied);
END IF;

Resources