varchar in execute immediate - oracle

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;
/

Related

Select Statement Using Dual Table As A Sub Query In Oracle Generates "From keyword not found" Error

I get below error message while executing following queries using a select statement from dual table as a sub query:
Error: ORA-00923: FROM keyword not found where expected
Query 1:
select a.dt_1, a.dt_2, a.dt_1=a.dt_2 as "match_type" from
(select to_date(replace('2020-05-14 00:00:00',' 00:00:00',''), 'yyyy/mm/dd') as "dt_1", to_date('14/05/2020','dd/mm/yyyy') as "dt_2" from dual) a
Query 2:
select a.dt_1, a.dt_2, a.dt_1=a.dt_2 as match_type from
(select to_date(replace('2020-05-14 00:00:00',' 00:00:00',''), 'yyyy/mm/dd') as dt_1, to_date('14/05/2020','dd/mm/yyyy') as dt_2 from dual) a
When I individually run sub query it executes as expected, however when I run the whole statement it generates error.
Any help is appreciated.
Your match_type column is generating the error. Oracle doesn't support relational operator matching. You may try below query -
SELECT a.dt_1,
a.dt_2,
CASE WHEN a.dt_1=a.dt_2 THEN 'TRUE' ELSE 'FLASE' END AS "match_type"
FROM (SELECT TO_DATE(REPLACE('2020-05-14 00:00:00',' 00:00:00',''), 'yyyy/mm/dd') as "dt_1",
TO_DATE('14/05/2020','dd/mm/yyyy') as "dt_2"
FROM DUAL) a;

Count of rows from all views in Oracle with a condition

I am trying to get count of all rows from views in oracle schema and my code is working fine. But when i try to add a condition like where actv_ind = 'Y', i am unable to get it working. Any suggestions on how to modify the code to make it working?
SELECT view_name,
TO_NUMBER(extractvalue(xmltype(dbms_xmlgen.getxml('select count(*) cnt from '||owner||'.'||view_name||
'where'||view_name||'.'||'actv_ind= Y')),'/ROWSET/ROW/CNT')) as VIEW_CNT
FROM all_views
WHERE owner = 'ABC' AND view_name not in ('LINK$')
I am getting the error ACTV_IND : Invalid Identifier.
The error messages from DBMS_XMLGEN are not very helpful so you need to be very careful with the syntax of the SQL statement. Add a space before and after the where, and add quotation marks around the Y:
SELECT view_name,
TO_NUMBER(extractvalue(xmltype(dbms_xmlgen.getxml('select count(*) cnt from '||owner||'.'||view_name||
' where '||view_name||'.'||'actv_ind= ''Y''')),'/ROWSET/ROW/CNT')) as VIEW_CNT
FROM all_views
WHERE owner = 'ABC' AND view_name not in ('LINK$');
The query still assumes that every view contains the column ACTV_IND. If that is not true, you might want to base the query off of DBA_TAB_COLUMNS WHERE COLUMN_NAME = 'ACTV_IND'.
Here's a simple sample schema I used for testing:
create user abc identified by abc;
create or replace view abc.view1 as select 1 id, 'Y' actv_ind from dual;
create or replace view abc.view2 as select 2 id, 'N' actv_ind from dual;

Issue while running a query within a procedure

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'[)]'

can I use `=` sign operator with sub-query instead of `IN`

I am just wondering if use = sign operator with sub-query instead of IN
Is it correct way ? and meet the oracle standard ?
Example
select column_name from my_table_1 where id = (select max(id) from my_table_2);
The Difference is related to the number of rows returned. If you have only one row returned from nested sql you may prefer both = or in operators. But if multiple rows returned from nested query, use in operator.
So, in your sql example you may prefer using any of the operators. Since, max functions returns only one row.
As you are fetching maximum value from subquery to compare with id, Both(= and IN )will work fine. But If you are trying to fetch more than one row then you have to use IN keyword.
If you have 1 result in sub query you are fine with using = sign, except when data type is wrong, for example , checking with same data type of dummy VARCHAR2(1)
select * from dual where 'X' = (select max(dual.dummy) from dual);
Is similar to using in (also same explain plain)
select * from dual where 'X' in (select max(dual.dummy) from dual);
But checking with different/wrong data type will result with exception ORA-01722 Invalid number
select * from dual where 1 =(select max(dual.dummy) 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