ORA-00907 while trying to create a table with automatic column - oracle

I'm attempting to create a table with an automatic column, the value of which is computed using a function I've defined. However, when I try to create the table I keep getting ora-00907: Missing right parenthesis. Can anyone help?
Here is the CREATE code:
CREATE TABLE NEW_EMP2 (
SSN CHAR(9),
EMP_NUM2 CHAR(5) automatic as newemp2id(SSN),
Fname VARCHAR2(15),
Lname VARCHAR2(15),
Bdate DATE
)
Here is the code for the function newemp2id:
CREATE OR REPLACE FUNCTION newemp2id (i_ssn NCHAR) RETURN NCHAR
IS
BEGIN
RETURN 'E'||(1000+SUBSTR(i_ssn,6,4));
END
Any help on this would be greatly appreciated, thanks!
UPDATE: I'm using Oracle Express Edition on a Windows Vista machine, in case that makes any difference.

I hadn't heard of the syntax prior to this, but all I could find is this PDF for Oracle RDB. RDB was/is a separate product for Oracle databases... Confirmed - not supported on 10g
Use a BEFORE INSERT trigger instead, because I don't believe the syntax you're using is valid for Oracle Express (10g effectively) - there's no mention in the CREATE TABLE or ALTER TABLE documentation.
I'm not fond of using triggers, I'd prefer to have a single stored procedure for inserting into given table(s) & only allow anyone to use the procedure rather than direct table access...
CREATE OR REPLACE TRIGGER newemp2_before_insert
BEFORE INSERT
ON new_mep2
FOR EACH ROW
BEGIN
-- Update created_by field to the username of the person performing the INSERT
:new.emp_num2 := newemp2id(new.ssn)
END;
Though frankly, this is overcomplicated when it could be handled in a view:
CREATE VIEW vw_emp AS
SELECT t.ssn,
'E'||(1000+SUBSTR(i_ssn,6,4)) AS emp_num2
FROM NEW_EMP2 t

What's an automatic column supposed to be? Did you mean a purely computed i.e. virtual column? Then your statement should look like this:
CREATE TABLE NEW_EMP2 (
SSN CHAR(9),
EMP_NUM2 CHAR(5) GENERATED ALWAYS AS ( newemp2id(SSN) ) VIRTUAL,
Fname VARCHAR2(15),
Lname VARCHAR2(15),
Bdate DATE
)
And your functions need to declared deterministic:
CREATE OR REPLACE FUNCTION newemp2id (i_ssn NCHAR) RETURN NCHAR DETERMINISTIC
IS
BEGIN
RETURN 'E'||(1000+SUBSTR(i_ssn,6,4));
END
If I'm not mistaken, virtual columns were introduced with Oracle 11g.

Oracle Express is Oracle 10g.
According to the manual (http://download-uk.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_7002.htm#i2095331) there is no "automatic" keyword and Oracle 10 has never supported "computed columns"
Oracle 11g supports virtual columns, but they are created using GENERATED ALWAYS, not even Oracle 11g has an automatic keyword
Why do you think this should work in Oracle?

Related

How to call Oracle stored procedure from azure data factory v2

My requirement is copy data from Oracle to SQL Server. Before copying from Oracle database, I need to update the Oracle table using procedure which has some logic.
How do I execute Oracle stored procedure from Azure datafactory?
I referred to this thread
if I use EXECUTE PROC_NAME (PARAM); in preCopy script it's failing with following error
Failure happened on 'Source' side.
ErrorCode=UserErrorOdbcOperationFailed,
Type=Microsoft.DataTransfer.Common.Shared.HybridDeliveryException
Message=ERROR [42000] [Microsoft][ODBC Oracle Wire Protocol driver]
[Oracle]ORA-00900: invalid SQL statement
Source=Microsoft.DataTransfer.ClientLibrary.Odbc.OdbcConnector,
Type=System.Data.Odbc.OdbcException
Message=ERROR [42000] [Microsoft][ODBC Oracle Wire Protocol driver]
[Oracle]ORA-00900: invalid SQL statement,Source=msora28.dll
Could anyone help on this?
Note: I am using self-hosted runtime environment for data factory
thanks!!
I used a Lookup Activity and a SELECT statement of DUAL TABLE. Due to the stored procedures can not be call from a statement SELECT. I created an oracle function and the function calls the stored procedure. The function returns a value and this value is received by the lookup activity.
When you define the function, you have to add the statement PRAGMA AUTONOMOUS_TRANSACTION. This is because Oracle does not allow to execute DML instructions with a SELECT statement by default. Then, you need to define that DML instructions in the Stored Procedure will be an autonomous transaction.
--Tabla
CREATE TABLE empleados(
emp_id NUMBER(9),
nombre VARCHAR2(100),
CONSTRAINT empleados_pk PRIMARY KEY(emp_id),
);
create or replace procedure insert_empleado (numero in NUMBER, nombre in VARCHAR2) is
begin
INSERT INTO empleados (emp_id, nombre)
Values(numero, nombre);
COMMIT;
end;
create or replace function funcinsert_empleado (numero in NUMBER, nombre in VARCHAR2)
return VARCHAR2
is
PRAGMA AUTONOMOUS_TRANSACTION;
begin
insert_empleado (numero, nombre);
return 'done';
end;
--statement in query of lookup
SELECT funcinsert_empleado ('1', 'Roger Federer')
FROM DUAL;
Example lookup
This is example in Spanish. https://dev.to/maritzag/ejecutar-un-stored-procedure-de-oracle-desde-data-factory-2jcp
In Oracle, EXECUTE X(Y) is a SQL*Plus-specific command shortcut for the PL/SQL statement BEGIN X(Y); END;. Since you are not using SQL*Plus, try the BEGIN/END syntax.
In case you only want to execute the DML query using the Azure Data Factory without procedure on oracle database :-
I have another solution where you can use the copy activity with the pre-copy feature of sink in-spite of lookup activity.
For this approach just follow the below steps :-
Keep both the source table and sink table as same ( Let say table A ) using the same linked service.
In sink use the pre-copy script feature and keep the DML (Insert/Update/Delete ) query that you want to perform over the table B.( This table is not necessary to be same as table A )
In case you want to avoid the copy of data to same table you can select query option in the source part and provide a where clause which is not going to satisfy and hence no copy of data will happen .
or you can create a table temp with one column and one row .
I have tested both the options and it works ... good part of above solution is you can avoid the procedure or function creation and maintenance .

ORACLE pass user defined object type as parameter to procedure

I am trying to attempt a bulk insert by using a FORALL in the Procedure .
I have tried the below steps to create the procedure :
**CREATE TYPE SECID_TABLE as TABLE OF VARCHAR2 INDEX BY NUMBER;**
CREATE PROCEDURE ASP_STOCK
(**p_secid IN SECID_TABLE**
) as
BEGIN
..
END;
But the above two statements do not compile. I am rather new to oracle and use aqua studio which doesnt seem to be verbose on the error statement.
Can someone please guide me?
There are two problems here.
The first problem is that you can't create an index-by table with an index type of NUMBER. SQLFiddle here Change the index type to PLS_INTEGER.
But even if you do THAT you're still going to get errors because an index-by table like this is a PL/SQL-only construct. SQLFiddle here.
You're going to have to do something else. Try an unindexed TABLE type, which is allowed at schema level. SQLFiddle here
Best of luck.

how to insert in oracle 10g database and returns the ID generated using stored procedure

I am very new to oracle's sql developer (since we've studied mysql) as well as in programming. I've searched in this website the answer to my question but I really can't understand the solutions provided.
What I want is to return the ID generated after inserting an object from java into the database. I'm using mybatis and oracle 10g database. I've already created the table and its columns.
Here's my code for the mapper
<insert id="addUser" parameterType="User" statementType="CALLABLE">
{ CALL addUserSP(
#{user.surname, javaType=String, jdbcType=VARCHAR, mode=IN},
#{user.firstName, javaType=String, jdbcType=VARCHAR, mode=IN},
#{userId, javaType=Integer, jdbcType=NUMBER, mode=OUT}
)}
</insert>
Here's my stored procedure (and I've already create a package named 'CREATEUSER')
PROCEDURE ADDUSERSP
( surname IN VARCHAR2,
firstName IN VARCHAR2,
userId OUT NUMBER
) AS
BEGIN
INSERT INTO users("surname", "first_name")
VALUES (surname, firstName);
RETURNING user_id INTO userId;
END ADDUSERSP;
According to what I've found here, it seems that I need to create a trigger(?) and sequence(?) to make the user_id auto increment whenever I add new data into the table. However, I have no idea how to do it.
Here are my questions:
Is my stored procedure right? Are the codes incomplete? I mean, I have not declared the package in the mapper and I've seen that it is needed (?), something like this { CALL [CreateUser].[addUserSP]( blah blah.... Should I write a sequence and trigger or there is an easy way to make the primary key user_id to be auto incremented? Kindly also check the syntax. I have a lot of problems in syntax.
Thank you so much!
To emulate MySQL AUTO_INCREMENT in Oracle, that pattern (as you found) does use a SEQUENCE object and a BEFORE INSERT trigger.
As a demonstration, something like this for the sequence object:
CREATE SEQUENCE myseq START WITH 1 INCREMENT BY 1 ;
And something like this for the before insert trigger:
CREATE TRIGGER users_bi
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
IF :NEW.id IS NULL THEN
SELECT myseq.NEXTVAL INTO :NEW.id FROM DUAL;
END IF;
END
As far as the procedure, I'm not a big fan of extra PL/SQL blocks that wrap a SQL INSERT statement.
It looks like you have an extra semicolon, before RETURNING. That clause is part of the INSERT statement, not a separate statement.
One big gotcha to be aware of is that SQL statements within a PL/SQL block can reference both columns and PL/SQL variables. When variables have the same names as columns, you will likely encounter behavior you didn't expect.
Typically PL/SQL author use a naming convention for variables that reduces the likelihood of name collisions. We frequently see variables with names like v_surname. (Personally, I use a slightly different convention, but the variable names "look like" variable names, not column references. And I don't name columns following the pattern I use for variables.)
The double quotes around the identifiers are acceptable, but this does make the identifiers case sensitive. When identifiers aren't enclosed in double quotes, Oracle treats them as if they were UPPER CASE. Just make sure that your table was defined with lower case column names.

How to use Oracle Collection of Record type in table?

I was trying to explore Oracle Collection and Record type. How do they work ? I wrote below script, but unable to compile them. What is wrong here ?
create or replace package pkg is
type ty_rec is record(empno emp.empno%type,ename emp.ename%type,sal emp.sal%type);
end pkg;
/
create or replace package body pkg is
end pkg;
/
create or replace type ty_varray is varray(5) of pkg.ty_rec;
/
create or replace type ty_table is table of pkg.ty_rec;
/
create table tab1(
id number,
col_arr ty_varray,
col_tab ty_table
) nested table col_tab store as tab1_col_tab;
/
Also, anyone could explain the Nested Table vs Varray when we are using them in table column. How do they stores data and which one is faster ?
Note: I'm using scott schema, which has default emp table
Records are a PL/SQL construct. This means they cannot be used in pure SQL statements. So you need to create the "record" type as a pure SQL object:
create or replace type ty_emp_rec as object
(empno number
,ename varchar2(20)
,sal number);
/
create or replace type ty_emp_varray is varray(5) of ty_emp_rec;
/
create or replace type ty_emp_table is table of ty_emp_rec;
/
I agree it would be highly neat if your code worked, not least because the ability to define attributes using the %TYPE syntax would be extremely useful. But alas that's restricted to the PL/SQL engine too.
This limitation is down to the ability to declare heap table columns with user-defined types, as your last example shows. Oracle requires its columns to be strongly-typed. The %TYPE syntax would create all sorts of problems here: consider what would happen if emp.empno changed from NUMBER to VARCHAR2, or vice versa.

CLOB vs. VARCHAR2 and are there other alternatives?

I am using DevArt's dotConnect and Entity Developer for my application. I've created the tables using the Entity-First feature.
I notice that many of the column types are set to CLOB. I only have experience with MySQL and Microsoft SQL server, so I am not sure about using CLOB for the application. I did some reading, and found out that CLOB are for large chunk of data.
The questions are:
Is using CLOB for most fields, such as the user's gender (which should be a varchar (1) ) or the full name, feasible? The steps for converting a CLOB field to VARCHAR2 requires dropping the column then re-creating it, and is buggy in DevArt's Entity Explorer, so I would like avoid it if possible. Edit: I just found out that if you set a maximum length for a string field it'll automatically be a VARCHAR2.
Are there any equivalents for TINYTEXT in Oracle?
It is a very bad idea to use a CLOB datatype for a column which ought to be VARCHAR2(1). Apart from the overheads (which are actually minimal, as Oracle will treat inline CLOBs of < 4000 characters as VARCHAR2) we should always strive to use the most accurate representation of our data in the schema: it's just good practice.
This really seems like a problem with the DevArt tool, or perhaps your understanding of how to use it (no offence). There ought to be some way for you to specify the datatype of an entity's attribute and/or a way of mapping those specifications to Oracle's physical datatypes. I apologise if this seems a little vague, I'm not familar with the product.
So, this is the basic problem:
SQL> desc t69
Name Null? Type
----------------------------------------- -------- --------
COL1 CLOB
SQL>
SQL> alter table t69 modify col1 varchar2(1)
2 /
alter table t69 modify col1 varchar2(1)
*
ERROR at line 1:
ORA-22859: invalid modification of columns
SQL>
We can fix it by using DDL to alter the table structure. Because the schema has many such columns it is worthwhile automating the process. This function drops the existing column and recreates it as a VARCHAR2. It offers the option to migrate data in the CLOB column to the VARCHAR2 column; you probably don't need this, but it's there for completeness. (This is not production quality code - it needs error handling, managing NOT NULL constraints, etc)
create or replace procedure clob2vc
( ptab in user_tables.table_name%type
, pcol in user_tab_columns.column_name%type
, pcol_size in number
, migrate_data in boolean := true )
is
begin
if migrate_data
then
execute immediate 'alter table '||ptab
||' add tmp_col varchar2('|| pcol_size|| ')';
execute immediate
'update '||ptab
||' set tmp_col = substr('||pcol||',1,'||pcol_size||')';
end if;
execute immediate 'alter table '||ptab
||' drop column '|| pcol;
if migrate_data
then
execute immediate 'alter table '||ptab
||' rename column tmp_col to '|| pcol;
else
execute immediate 'alter table '||ptab
||' add '||pcol||' varchar2('|| pcol_size|| ')';
end if;
end;
/
So, let's change that column...
SQL> exec clob2vc ('T69', 'COL1', 1)
PL/SQL procedure successfully completed.
SQL> desc t69
Name Null? Type
----------------------------------------- -------- ---------------
COL1 VARCHAR2(1)
SQL>
Calling this procedure can be automated or scripted in the usual ways.
Using a CLOB for something like a Gender column would, at a minimum, be extremely unusual. If the DDL this tool generates specifies that the LOB data should be stored inline rather than out of line, I wouldn't expect to be any horrible performance issues. But you probably will create problems for other tools accessing the database that don't handle LOBs particularly well.
There is no equivalent in Oracle to Tinytext in MySQL. A CLOB is a CLOB.
A simpler solution is to go to Model Explorer -> Model.Store -> Tables/Views, find the necessary column and change the type of this field to VARCHAR2.
Then run the Update Database from Model wizard to persist the changes to the database.
Don't forget to set the MaxLength facet (however, the problem with it is already fixed in the upcoming Beta build).

Resources