ORA-00922: missing or invalid option when trying to create column in table - oracle

I'm using the following code for create a column in an existing table, but, I'm getting this error:
ORA-00922: missing or invalid option
I've tried get the desired result (create column in table "only if this column does not exists") without the EXECUTE IMMEDIATE instruction, but, PL/SQL doesn't allow use the ALTER TABLE [...] in an IF [] THEN structure.
Is there something I'm missing?
This is the db<>fiddle sample:
CREATE TABLE "TMP_TABLE_SAMPLE"
( "ID_TABLE" NUMBER(9,0)
) ;
✓
SET SERVEROUTPUT ON;
CLEAR SCREEN;
DECLARE
V_COLUMN_EXISTS NUMBER := 0;
BEGIN
SELECT COUNT(1) CONTEO
INTO V_COLUMN_EXISTS
FROM USER_TAB_COLS
WHERE UPPER(COLUMN_NAME) = 'PNT_NCODE'
AND UPPER(TABLE_NAME) = 'TMP_TABLE_SAMPLE';
IF V_COLUMN_EXISTS = 0 THEN
EXECUTE IMMEDIATE 'ALTER TABLE TMP_TABLE_SAMPLE ADD PNT_NCODE NUMBER (9,0) ' ||
' COMMENT ON COLUMN TMP_TABLE_SAMPLE.PNT_NCODE IS ''Stores ID from TMP_TABLE_SAMPLE_2.''';
ELSE
DBMS_OUTPUT.PUT_LINE('Column already exists');
END IF;
END;
ORA-00922: missing or invalid option

The error you're getting is because you have set serveroutput on and clear screen in your script. db<>fiddle knows how to interpret SQL and PL/SQL. It doesn't support SQL*Plus commands.
If you remove those, the next error you'll get is that you have a single execute immediate statement that is trying to execute two separate statements. Creating the column and adding a comment on the column are separate operations so you need separate statements.
If I change your fiddle to this, it works the way you want
DECLARE
V_COLUMN_EXISTS NUMBER := 0;
BEGIN
SELECT COUNT(1) CONTEO
INTO V_COLUMN_EXISTS
FROM USER_TAB_COLS
WHERE UPPER(COLUMN_NAME) = 'PNT_NCODE'
AND UPPER(TABLE_NAME) = 'TMP_TABLE_SAMPLE';
IF V_COLUMN_EXISTS = 0 THEN
EXECUTE IMMEDIATE 'ALTER TABLE TMP_TABLE_SAMPLE ADD PNT_NCODE NUMBER (9,0) ';
EXECUTE IMMEDIATE 'COMMENT ON COLUMN TMP_TABLE_SAMPLE.PNT_NCODE IS ''Stores ID from TMP_TABLE_SAMPLE_2.''';
ELSE
DBMS_OUTPUT.PUT_LINE('Column already exists');
END IF;
END;
/

Related

Encountered symbol "DROP" when expecting one of the following

Hi I am writing a simple set of oracle statements but I am getting an error saying "PLS-00103: Encountered the symbol DROP when expecting one of the following". I am not sure what is wrong with my statements. Any help is appreciated.
DECLARE
table_exists number := 0;
BEGIN
SELECT COUNT(*) INTO table_exists FROM dba_tables WHERE owner = 'ABC'
AND table_name = 'XYZ';
If (table_exists = 1) then
DROP TABLE "ABC"."XYZ";
End If;
End;
If you want use DDL statements in PL/SQL blocks, you have to use dynamic SQL.
Try this:
execute immediate 'DROP TABLE ' || owner.table_name

Oracle: Using EXECUTE IMMEDIATE with RETURNING INTO

I have a procedure that does a validation and inserts a record in a table. The procedure is breaking right after the INSERT statement when I try the following code:
EXECUTE IMMEDIATE V_SOME_STRNG || ' returning SOME_ID into :NEW_ID' returning into V_TRGT_ID;
I am trying to execute my INSERT statement which is stored in V_SOME_STRNG and assign the new record's ID to V_TRGT_ID. However, I am running into the following error:
ORA-00933: SQL command not properly ended
Any thoughts?
You don't need to repeat the returning into part, you need a using clause for your bind variable:
EXECUTE IMMEDIATE V_SOME_STRNG || ' returning SOME_ID into :NEW_ID' using out V_TRGT_ID;
Demo using a basic trigger to provide the ID:
create table t42 (some_id number, dummy varchar2(1));
create sequence s42 start with 42;
create trigger tr42 before insert on t42 for each row
begin
:new.some_id := s42.nextval;
end;
/
set serveroutput on
declare
v_some_strng varchar2(200) := 'insert into t42 (dummy) values (''X'')';
v_trgt_id number;
begin
EXECUTE IMMEDIATE V_SOME_STRNG || ' returning SOME_ID into :NEW_ID' using out V_TRGT_ID;
dbms_output.put_line('Returned ID: ' || v_trgt_id);
end;
/
which shows:
Returned ID: 42
PL/SQL procedure successfully completed.
You can only use returning into with the insert .. values ... pattern, not with insert ... select ...; so for instance changing the code above to use;
v_some_strng varchar2(200) := 'insert into t42 (dummy) select ''X'' from dual';
will get the error you originally reported:
ORA-00933: SQL command not properly ended
ORA-06512: at line 6
While you don't need to use returning into part, the OP problem most likely results from an error in the not shown content of the V_SOME_STRNG variable. Because you definitely can use returning into with execute immediate. Here is an example strait from the documentation:
sql_stmt := 'UPDATE emp SET sal = 2000 WHERE empno = :1 RETURNING sal INTO :2';
EXECUTE IMMEDIATE sql_stmt USING emp_id RETURNING INTO salary;
I stress the point again: it works. So if you have any troubles here check you dynamically generated SQL statement more thoroughly.
My test_queries table consist of 2 columns:fid and query_text.I want insert new row. And I return fid I inserted because I use it next question. But the code give me error .
select max(a.fid) into max_fid from test_queries a;
execute immediate 'insert into test_queries values (:1,:2) returning fid into :a' using max_fid+1,query_text,c;

Compile error oracle procedure

Hej. I have a task that says to create a procedure that adds column "BRUTTO" to a table "TABELA_1, then fills that column with values based on values from column "NETTO" and output all records from TABLE_1, including newly created BRUTTO. It works without commented out code but doesn't otherwise. Apparently it doesn't see column BRUTTO yet so I can't reference it like that. Any help appreciated.
CREATE OR REPLACE PROCEDURE WSTAW_BRUTTO_I_WYSWIETL
AS
--CURSOR C IS
--SELECT NAZWISKO, NETTO, BRUTTO FROM TABELA_1;
V_VAT NUMBER(9,2) := 24;
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE TABELA_1 ADD BRUTTO NUMBER';
EXECUTE IMMEDIATE 'UPDATE TABELA_1 SET BRUTTO = NETTO * (1 + :1 /100)' USING V_VAT;
--FOR V_REC IN C
--LOOP
--DBMS_OUTPUT.PUT_LINE('| '||V_REC.NETTO||' | '||V_REC.BRUTTO);
--END LOOP;
END WSTAW_BRUTTO_I_WYSWIETL;
Your procedure won't compile because you can't access a column before it is added to the table. Not sure why you wrote a procedure with dynamic SQL for this. A plain SQL statement should work. Moreover, you can't use bind variable in a DDL, It would have raised
ORA-01027: bind variables not allowed for data definition operations
during run time.
You should also consider using BRUTTO as a VIRTUAL COLUMN, rather than a column itself.
ALTER TABLE TABELA_1 ADD BRUTTO NUMBER AS ( NETTO * (1 + 24 /100) );
Demo
If you still think you want a procedure and it has to compile, you should put the block inside EXECUTE IMMEDIATE, but it's not recommended.
CREATE OR REPLACE PROCEDURE wstaw_brutto_i_wyswietl AS
v_vat NUMBER(9,2) := 24;
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE TABELA_1 ADD BRUTTO NUMBER AS ( NETTO * (1 + '
|| v_vat
|| ' /100) )';
EXECUTE IMMEDIATE q'{BEGIN
FOR V_REC IN ( SELECT NETTO,BRUTTO FROM TABELA_1 )
LOOP
DBMS_OUTPUT.PUT_LINE(V_REC.NETTO||','||V_REC.BRUTTO);
END LOOP;
END;}'
;
END wstaw_brutto_i_wyswietl;
/
Demo2

Displaying command entered to run the trigger

I am trying to figure out how to display the insert statement, which the user enters. I want it to display after the "Please update the insert statement" text prints. From reading a ton of things online, I found out that you can display the previous command entered on oracle, by entering a "/" sign, and also by running this query 'SELECT * FROM gv$sql WHERE SQL_ID = IDENT_CURRENT('gv$sql')'. I tried using an execute immediately statement in the trigger, using dbms_output.put_line(/), and simply using t0_char('/'); in the query as you see below. Any tips?
set serveroutput on
CREATE or REPLACE trigger before_insert_t
before insert on reservations
for each row
DECLARE
rooms_remaining number(5,2);
BEGIN
select roooms_rem into rooms_remaining from reservations where roomno=:new.roomno;
if rooms_remaining = 0 then
dbms_output.put_line('Insertion now allowed because room ' || :new.roomno || ' is booked!' );
dbms_output.put_line('Please update the insert statement');
-- to_char('/');
dbms_output.put_line('insert into reservations values ' || :new.roomno );
-- EXECUTE IMMEDIATE sql_stmt;
end if;
END;
/
show errors
insert into reservations values (99,9);
CREATE or REPLACE trigger before_insert_t
before insert on TEST_TAB1
for each row
DECLARE
sql_insert varchar2(1000);
BEGIN
select sql_text into sql_insert
from (select sql_text
from v$sql
where upper(sql_text) like 'INSERT INTO TEST_TAB1%'
order by first_load_time desc)
where rownum=1;
dbms_output.put_line('Inserting into table SQL is '||sql_insert);
END;
/
SQL> set serveroutput on
SQL> insert into TEST_TAB1 values ('Hello');
1 rows inserted.
Inserting into table SQL is insert into TEST_TAB1 values (:"SYS_B_0")
You order by FIRST_LOAD_TIME and sort it in descending order and select the first row which would be the most recent INSERT statement in case there are multiple INSERTs.

oracle drop index if exists

How do you drop an index only if it exists?
It seems simple but I did found anything on the net.
The idea is to drop it only if it exists, because if not, I will have an error and my process stops.
I found this to find if the index exists:
select index_name
from user_indexes
where table_name = 'myTable'
and index_name='myIndexName'
But I don't know how to put it together with
DROP INDEX myIndexName
Don't check for existence. Try to drop, and capture the exception if necessary...
DECLARE
index_not_exists EXCEPTION;
PRAGMA EXCEPTION_INIT (index_not_exists, -1418);
BEGIN
EXECUTE IMMEDIATE 'drop index foo';
EXCEPTION
WHEN index_not_exists
THEN
NULL;
END;
/
DECLARE
COUNT_INDEXES INTEGER;
BEGIN
SELECT COUNT ( * )
INTO COUNT_INDEXES
FROM USER_INDEXES
WHERE INDEX_NAME = 'myIndexName';
-- Edited by UltraCommit, October 1st, 2019
-- Accepted answer has a race condition.
-- The index could have been dropped between the line that checks the count
-- and the execute immediate
IF COUNT_INDEXES > 0
THEN
EXECUTE IMMEDIATE 'DROP INDEX myIndexName';
END IF;
END;
/
In Oracle, you can't mix both DDL and DML. In order to do so, you need to work it around with the EXECUTE IMMEDIATE statement.
So, first check for the existence of the index.
Second, drop the index through the EXECUTE IMMEDIATE statement.
DECLARE v_Exists NUMBER;
BEGIN
v_Exists := 0;
SELECT 1 INTO v_Exists
FROM USER_INDEXES
WHERE TABLE_NAME LIKE 'myTable'
AND INDEX_NAME LIKE 'myIndexName'
IF v_Exists = 1 THEN
EXECUTE IMMEDIATE "DROP INDEX myIndexName"
ENDIF;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
This code is out the top of my head and you may need to fix it up a little, but this gives an idea.
Hope this helps! =)
I made a procedure so it can be called several times:
DELIMITER €€
DROP PROCEDURE IF EXISTS ClearIndex€€
CREATE PROCEDURE ClearIndex(IN var_index VARCHAR(255),IN var_table VARCHAR(255))
BEGIN
SET #temp = concat('DROP INDEX ', var_index, ' ON ', var_table);
PREPARE stm1 FROM #temp;
BEGIN
DECLARE CONTINUE HANDLER FOR 1091 SELECT concat('Index ', var_index,' did not exist in ',var_table,', but was handled') AS 'INFO';
EXECUTE stm1;
END;
END €€
DELIMITER ;
Now it can be called more than once:
CALL ClearIndex('employees_no_index','employees');
CALL ClearIndex('salaries_no_index','salaries');
CALL ClearIndex('titles_no_index','titles');
I hope this will help. It's a combination of all solution :)
By the way thanks for the help !
CREATE OR REPLACE PROCEDURE CLEAR_INDEX(INDEX_NAME IN VARCHAR2) AS
BEGIN
EXECUTE IMMEDIATE 'drop index ' || INDEX_NAME;
EXCEPTION
WHEN OTHERS THEN
NULL;
END CLEAR_INDEX;

Resources