ORA-00904: invalid identifier during merge PLSQL - oracle11g

I am trying to run merge statement in following manner in PLSQL block and getting ORA-00904 error described below:
--Merge statement
l_mergestr1:='merge into pi_customer picu using (select * from table(picu_var)) src ' ||
' on (picu.customer_id = src.customer_id) when matched then update set '||l_newmergeupstr1||
' where picu.time_stamp <> pctemp.time_stamp when not matched then' ||
' insert ('||l_newmergeinsstr1||') values ('||l_newmergeinstvalstr1||')';
execute immediate l_mergestr1;
ORA-00904: "PICU_VAR": invalid identifier
Here picu_var is a variable of object_type which I have already declared.
Using dbms_output I verified the merge statement converts to following which I think is a correct syntax for merge and it works :
merge into pi_customer picu using (select * from table(picu_var)) src
on (picu.customer_id = src.customer_id) when matched then update set picu.Customer_Name=src.Customer_Name,picu.Server_Name=src.Server_Name,picu.Time_stamp=src.Time_stamp
where picu.time_stamp <> src.time_stamp when not matched then
insert (picu.Customer_ID,picu.Customer_Name,picu.Server_Name,picu.Time_stamp) values (src.Customer_ID,src.Customer_Name,src.Server_Name,src.Time_stamp)
I also verified there is no mismatch across source and target table column names and the merge syntax works when executed as what returned from dbms_output.
Please suggest how to avoid the error ORA-00904.
Does the syntax for merge declared in "l_mergestr1" requires a change?

You can't reference a variable from outside the dynamic SQL inside the dynamic SQL. Use a bind variable to pass it in:
l_mergestr1:='merge into pi_customer picu using (select * from table(:picu_bv)) src ' ||
' on (picu.customer_id = src.customer_id) when matched then update set '||l_newmergeupstr1||
' where picu.time_stamp <> pctemp.time_stamp when not matched then' ||
' insert ('||l_newmergeinsstr1||') values ('||l_newmergeinstvalstr1||')';
execute immediate l_mergestr1 using picu_var;

Related

In SQLite , How to SELECT a column only if it exists in the table

I am trying to write a query where the table will be generated dynamically for each job . And the columns will either exist or not based on input. In SQLite , i need to fetch the value of a column only if it exists otherwise null.
I tried with if & case statements using Pragma_table_info , but for negative scenario it is not working.
'''select
case when (select name from pragma_table_info('table_name') where name = col_name )is null
then error_message
else col_name'''
end
from table_name
This query is running if the mentioned col_name exists . But if not exists then it is throwing syntax error in else part.
Only in select query it should be done
Your code should work if the table's name, the column's name and the error message are properly quoted:
SELECT CASE
WHEN (SELECT name FROM pragma_table_info('table_name') WHERE name = 'col_name')
IS NULL THEN 'error_message'
ELSE 'col_name'
END
But you can do the same simpler with aggregation and COALESCE():
SELECT COALESCE(MAX(name), 'error_message')
FROM pragma_table_info('table_name')
WHERE name = 'col_name'
See the demo.

Stored Procedure returns "Error Code: 1054. Unknown column 'schema.table.col' in 'field list'" when Creating different temporary table on same session

When using a stored procedure to dynamically generate a table based on configuration and return a result set (SELECT) with the records in that table, the second call to that procedure to generate a different table structure returns no records and it reports a missing column from a previous temporary table of the same name.
I tried this with MariaDB 10.3 and 10.1.21 and received the same result. I have minimized my code here to the minimum to demonstrate the error after trying several variations of single and multiple sub-procedures.
I also tried using some transaction control with COMMITS after executing the process, before trying to start the process with a different parameter, but got the same results.
DROP PROCEDURE IF EXISTS CreateATable;
DELIMITER $$
CREATE PROCEDURE CreateATable( _TableType tinyint )
BEGIN
DROP TEMPORARY TABLE IF EXISTS aTable;
IF _TableType = 1 THEN
SET #SQL_Statement :=
CONCAT(
'CREATE TEMPORARY TABLE aTable (',
'the_id bigint, ',
'the_column varchar(100) ',
') engine=INNODB',
';');
ELSE
SET #SQL_Statement :=
CONCAT(
'CREATE TEMPORARY TABLE aTable (',
'the_id bigint, ',
'the_other_column varchar(100) ',
') engine=INNODB',
';');
END IF;
PREPARE stmtCreateTable FROM #SQL_Statement;
EXECUTE stmtCreateTable;
DEALLOCATE PREPARE stmtCreateTable;
SET #SQL_Statement := NULL;
END$$
DELIMITER ;
DROP PROCEDURE IF EXISTS GetATable;
DELIMITER $$
CREATE PROCEDURE GetATable()
BEGIN
CALL CreateATable( 1 );
SELECT * FROM aTable;
CALL CreateATable( 2 );
SELECT * FROM aTable;
END$$
DELIMITER ;
DROP PROCEDURE IF EXISTS GetATable2;
DELIMITER $$
CREATE PROCEDURE GetATable2(_TableType tinyint)
BEGIN
CALL CreateATable( _TableType );
SELECT * FROM aTable;
END$$
DELIMITER ;
/*
Test execution script starts here
*/
-- Just CALL Create for one and Select
CALL CreateATable( 1 );
DESCRIBE aTable;
SELECT * FROM aTable;
CALL CreateATable( 2 );
DESCRIBE aTable;
SELECT * FROM aTable;
-- -> no errors
-- now CALL procedure to Create and Select from two different temp tables
CALL GetATable();
-- -> no errors
-- now CALL procedure to CREATE AND SELECT from ONE temp table definition using a parameter to select
CALL GetATable2(1);
CALL GetATable2(2);
-- Error Code: 1054. Unknown column 'mySchema.aTable.the_column' in 'field list'
I would expect that I can pass a parameter to a stored procedure to generate a temporary table, and return the records of that temporary table. Even if I call that same procedure multiple times with different parameters on the same session.
The actual results are that when the stored procedure is called to generate the temporary table with a different table structure, it returns this error complaining about the column missing from the temporary table created in the previous invocation of that same stored procedure.
Error Code: 1054. Unknown column 'mySchema.aTable.the_column' in 'field list'
The only way I have found to prevent this error is
a. ending the jdbc connection and ending the server session
b. recompiling one of the stored procedures in the call stack
Recompiling is not viable. And ending the session seems unreasonable.
This seems like a defect. But would be interested to find if there is some way to get this to work.
This seems like a bug and you can report it directly to the MariaDB team at MariaDB bugs database.
A temporary solution is to use a prepared statement in the stored procedure GetATable2 (my test on MariaDB 10.3.16 to use EXECUTE IMMEDIATE):
...
CREATE PROCEDURE `GetATable2`(`_TableType` TINYINT)
BEGIN
CALL CreateATable(`_TableType`);
-- SELECT * FROM `aTable`;
EXECUTE IMMEDIATE 'SELECT * FROM `aTable`';
END$$
...
See dbfiddle.

Create a table as, where 'date condition' in dynamic PL/SQL

I got assigned the following task.
Assume we have a table A structured with an id column and a date column.
Write a procedure in PL/SQL that: takes as parameters the table name (in our case A) and a date D, creates a backup table named A_bck containing only the records of A with dates < D and removes from the table A all the records inserted in A_bck.
Here there is my code.
Unluckily I get this error:
Error report -
ORA-00904: "MAY": invalid identifier
ORA-06512: at line 41
ORA-06512: at line 80
00904. 00000 - "%s: invalid identifier"
If I try to achieve the same result using a where condition on the id column instead that on the date one, I have no problems.
Where is the mistake? Am I implementing it completely in the wrong way?
The problem you have is that as you're executing dynamic sql you're query is built up as a string. Oracle does not know that the date you've given is actually a date, it is simply being treated as part of the string. To solve this you should be able to do the following:
my_query := 'CREATE TABLE ' || table_name_backup || ' AS (SELECT * FROM ' || table_name || ' WHERE table_date < to_date(''' || backup_date || '''))';
This should sort out your issue for you. As a side note, you will probably want to change your "table_exists" query, as table names are all stored in upper case, e.g.
SELECT COUNT(*) INTO table_exists FROM USER_TABLES WHERE TABLE_NAME = upper(my_table);
Edit: Further explanation following comment
To explain why you don't have the above problem when using integers, it is important to remember that using execute immediate simply executes the given string as an SQL query.
For example:
declare
x INTEGER := 1;
i integer;
my_query VARCHAR2(256);
begin
my_query := 'select 1 from dual where 1 = ' || x;
EXECUTE IMMEDIATE my_query INTO i;
end;
my_query in the above example would be executed as:
select 1 from dual where 1 = 1
which is perfectly valid sql. In your example however, you were ending up with something like this:
CREATE TABLE abaco_bck AS (SELECT * FROM abaco WHERE table_date < 27-MAY-17)
As it isn't wrapped in quotes, or explicitly converted to a date, the SQL engine is trying to subtract "MAY" from 27, but it doesn't know what "MAY" is.
One other thing to mention, is that for some operations you could use bind variables instead of quotes (although you can't for DDL) e.g.
declare
lToday DATE := SYSDATE;
i INTEGER;
my_query VARCHAR2(256);
begin
my_query := 'select 1 from dual where sysdate = :1';
EXECUTE IMMEDIATE my_query INTO i USING lToday;
end;

Convert NULL to Blank TPT Fast Load

Insert into emp values
(:FNAME ,.......
Above Sample Code from TPT works fine.
I want to convert null values in flatfile to blank while loading
insert into emp values ( COALESCE(:Fname,' '),.... -- Throws ERROR
TPT_INFRA: TPT04046: Error: Line 193 of Job Script File 'tpscript4.txt': Adjacen
t quoted strings must be separated by the
concatenation operator: '||'.
Job script preprocessing failed.
insert into emp Values ( case when :Fname is null then ' ' else :Fname End,... --Throws Error
Teradata Parallel Transporter Version 13.10.00.02
TPT_INFRA: TPT04046: Error: Line 191 of Job Script File 'tpscript4.txt': Adjacen
t quoted strings must be separated by the
concatenation operator: '||'.
Job script preprocessing failed.
Job terminated with status 8.
When used Case when in Select oerator for fastload:
TO OPERATOR (UPDATE_OPERATOR[2])
SELECT case when FNAME is null then ' ' else FNAME,LNAME,....
FROM OPERATOR (FILE_READER[2]);
ERROR:
TPT_INFRA: Syntax error at or near line 249 of Job Script File 'tpscript4.txt':
TPT_INFRA: At "SELECT" missing SEMICOL_ in Rule: Job Definition Body
Compilation failed due to errors. Execution Plan was not generated.
Job script compilation failed.
Job terminated with status 8.
Note: with out the case in select it is working fine ,
APPLY('insert into emp values ( COALESCE(:Fname,'' ''),....') Worked with Mload
and
SELECT CASE WHEN Fname IS NULL THEN ' ' ELSE Fname END AS Fname,... FROM OPERATOR worked with Fload
You didn't specify which error is returned, i assume it's related to the single quotes. Your INSERT is probably within an APPLY('INSERT ....;') you might try two single quotes to get one quote in a string:
APPLY('insert into emp values ( COALESCE(:Fname,'' ''),....')
Or do it in the SELECT (COALESCE is not supported here):
SELECT
CASE WHEN Fname IS NULL THEN ' ' ELSE Fname END AS Fname,
...
FROM OPERATOR

Invalid table name error while using Execute Immediate statement with bind variables

I'm trying to get this dynamic SQL running ( using EXECUTE IMMEDIATE)
M_SQL_STATEMENT := 'SELECT MAX(:m_var1)+1 from :m_var2 RETURNING MAX(:m_var1)+1 INTO :m_var3';
EXECUTE IMMEDIATE M_SQL_STATEMENT
USING M_COLUMN_NAME, UPPER(P_TABLE_NAME), M_COLUMN_NAME
RETURNING INTO M_SEQ_NUMBER;
However, when trying to run this, I keep running into
ORA-00903: Invalid table
P_TABLE_NAME is a table name which is accepted as an input. I have confirmed that the table name & the column name are valid. I can't figure out why Oracle is throwing the error.
FWIW Altering the SQL statement to
M_SQL_STATEMENT := 'SELECT MAX(:m_var1)+1 SEQ from :m_var2 RETURNING SEQ INTO :m_var3';
still results in the same error.
You need to put the table name and column name into the dynamic SQL, so something like
M_SQL_STATEMENT := 'SELECT MAX(' || M_COLUMN_NAME || ')+1 from '
|| P_TABLE_NAME';
EXECUTE IMMEDIATE M_SQL_STATEMENT INTO M_SEQ_NUMBER;

Resources