Have problem making SQL table on Apex Oracle - sql

I'm making two tables, STUDENT and STUDENTREPORT. I made STUDENT, but not the other. I ran the code, but it says missing or invalid option.
My STUDENTREPORT command:

Both commands are correctly written, they execute OK in e.g. SQL*Plus or SQL Developer:
SQL> CREATE TABLE studentreport (
2 sr_number VARCHAR2(5),
3 sr_rade VARCHAR2(5),
4 sr_semester VARCHAR2(5),
5 class_attended NUMBER,
6 s_id VARCHAR2(5),
7 PRIMARY KEY ( sr_number )
8 USING INDEX enable
9 );
Table created.
SQL> ALTER TABLE studentreport
2 ADD FOREIGN KEY ( s_id )
3 REFERENCES student ( s_id )
4 ENABLE;
Table altered.
SQL>
But, in Oracle Apex' SQL Workshop, you can execute only one command at a time. Therefore:
remove alter table (delete it from the editor) so that you'd first execute create table; then delete that statement and execute alter table, or
select (with a mouse, so that text turns blue) create table and hit RUN to execute it; then select alter table and execute it with RUN
That's just how SQL Workshop behaves, there's nothing you can do about it (at least, I don't know what you could/should do, apart from what I already said).

Related

In SQL, can I combine two or more fields into one field during the same insert statement?

For this scenario, I have a table like this: ID (Autoincrement, PK), PartType (VarChar), and DesignItemID (VarChar). I would like to combine the columns ID and PartType into column DesignItemID using a single INSERT statement.
Is this possible?
The purpose for this scenario spawns from trying to use an external SQL database for a part library in Altium Designer. Altium Designer needs a unique ID to maintain a proper link to the part that is placed and the DB. Ordinarily, an autoincrement PK could work, however, I need to keep the different types of parts in separate tables (such at resistors in a resistor table and capacitors in a capacitor table, etc.). So, if I have two or more different tables with an autoincrement PK ID column, I will have multiple IDs all starting at 1.
My proposed solution is to make a table with column ID using autoincrement for the PK, column PartType using a char or varchar, and column DesignItemID also using a char or varchar. Upon an INSERT command, I will enter the value RES for resistor or CAP for capacitor for column PartType and somehow LPAD ID to about 6 places and CONCAT with PartType to create DesignItemID RES000001 or CAP000001 for example. Both tables have 1 as PK ID, but, with the part type and padding, a unique column can be made for Altium Designer.
I understand that in a SQL admin interface, I could structure a query to create this unique piece of data, but Altium Designer requires this unique ID to be in a column.
I can accomplish this task in Access by using a calculate field, but Access is limited to number of concurrent users and cannot scale like an external SQL DB can.
Please note that I will have far more columns in the Database that corresponds to the part. I am only focusing on the columns that I do not know if what I am asking can be done.
depending on your database,
it seems you are asking for a unique number that spans across multiple tables. This could be called ultimately a GUID - if it should also be unique across databases.
this could be done with a single SEQUENCE. or you can look up GUID generators.
exporting multiple tables with such a GUID would be no problem - you just query from wherever they live and send them to your output stream.
Importing on the other hand is more difficult - since you will need to know where each GUID lives (in which table). You can do this with another table that maps each GUID to the table it belongs in.
A little bit of walking instead of just talking. Code you'll see is Oracle, but I guess other databases offer the same or similar options. Note that I don't know Altium Designer.
Question you asked was:
can I combine two or more fields into one field during the same insert statement?
Yes, you can; you already know the operator - it is concatenation. In Oracle, it is either the concat function or double pipe || operator. Here's how.
First, two sample tables (resistors and capacitors):
SQL> create table resistor
2 (id_res varchar2(10) constraint pk_res primary key,
3 name varchar2(10) not null
4 );
Table created.
SQL> create table capacitor
2 (id_cap varchar2(10) constraint pk_cap primary key,
3 name varchar2(10) not null
4 );
Table created.
Sequence will be used to create unique numbers:
SQL> create sequence seqalt;
Sequence created.
Database trigger which creates the primary key value by concatenating a constant (RES for resistors) and the sequence number, left-padded with zeros up to 7 characters in length (so that the full value length is 10 characters):
SQL> create or replace trigger trg_bi_res
2 before insert on resistor
3 for each row
4 begin
5 :new.id_res := 'RES' || lpad(seqalt.nextval, 7, '0');
6 end trg_bi_res;
7 /
Trigger created.
SQL> create or replace trigger trg_bi_cap
2 before insert on capacitor
3 for each row
4 begin
5 :new.id_cap := 'CAP' || lpad(seqalt.nextval, 7, '0');
6 end trg_bi_cap;
7 /
Trigger created.
Let's insert some rows:
SQL> insert into resistor (name) values ('resistor 1');
1 row created.
SQL> select * from resistor;
ID_RES NAME
---------- ----------
RES0000001 resistor 1
Capacitors:
SQL> insert into capacitor (name) values ('capac 1');
1 row created.
SQL> insert into capacitor (name) values ('capac 2');
1 row created.
SQL> select * From capacitor;
ID_CAP NAME
---------- ----------
CAP0000002 capac 1
CAP0000003 capac 2
My suggestion is a view instead of a new table to be used by the Altium Designer - of course, if it is possible (maybe Designer requires a table, and nothing but a table ...):
SQL> create or replace view v_altium (designitemid, name) as
2 select id_res, name from resistor
3 union all
4 select id_cap, name from capacitor;
View created.
SQL> /
View created.
SQL> select * from v_altium;
DESIGNITEM NAME
---------- ----------
RES0000001 resistor 1
CAP0000002 capac 1
CAP0000003 capac 2
You'd now make the Altium Designer read the view and - from my point of view - it should work just fine.
If it has to be a table (let's call it altium), then it would look like this:
SQL> create table altium
2 (designitemid varchar2(10) constraint pk_alt primary key,
3 name varchar2(10)
4 );
Table created.
Triggers will now be changed so that they also insert a row into the altium table (see line #7):
SQL> create or replace trigger trg_bi_res
2 before insert on resistor
3 for each row
4 begin
5 :new.id_res := 'RES' || lpad(seqalt.nextval, 7, '0');
6 insert into altium (designitemid, name) values (:new.id_res, :new.name);
7 end trg_bi_res;
8 /
Trigger created.
SQL> create or replace trigger trg_bi_cap
2 before insert on capacitor
3 for each row
4 begin
5 :new.id_cap := 'CAP' || lpad(seqalt.nextval, 7, '0');
6 insert into altium (designitemid, name) values (:new.id_cap, :new.name);
7 end trg_bi_cap;
8 /
Trigger created.
Let's try it:
SQL> insert into resistor (name) values ('resistor 4');
1 row created.
SQL> insert into resistor (name) values ('resistor 5');
1 row created.
SQL> insert into capacitor (name) values ('capac 5');
1 row created.
Altium table contents reflects contents of resistor and capacitor:
SQL> select * from altium;
DESIGNITEM NAME
---------- ----------
RES0000011 resistor 4
RES0000012 resistor 5
CAP0000013 capac 5
SQL>
However: why do I prefer a view over a table? Because consistency might suffer. What if you delete a row from the capacitor table? You'd have to delete appropriate row from the new altium table as well, and vice versa.
You can't create a foreign key constraint from the altium table to reference primary keys in other tables because as soon as you try to insert a row into the altium table that references resistor, it would fail as there's no such a primary key in capacitor. You can create constraints, but - that's pretty much useless:
SQL> drop table altium;
Table dropped.
SQL> create table altium
2 (designitemid varchar2(10) constraint pk_alt primary key,
3 name varchar2(10),
4 --
5 constraint fk_alt_res foreign key (designitemid) references resistor (id_res),
6 constraint fk_alt_cap foreign key (designitemid) references capacitor (id_cap)
7 );
Table created.
OK, table was successfully created, but - will it work?
SQL> insert into resistor (name) values ('resistor 7');
insert into resistor (name) values ('resistor 7')
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.FK_ALT_CAP) violated - parent key not
found
ORA-06512: at "SCOTT.TRG_BI_RES", line 3
ORA-04088: error during execution of trigger 'SCOTT.TRG_BI_RES'
SQL>
Nope, it won't as such a primary key doesn't exist in the capacitor table.
It means that you'd have to maintain consistency manually, and that's always tricky.
Therefore, if possible, use a view.

PL/SQL developer how to get the row that made the insert fail?

I'm doing a method that inserts into the table which has a unique column. What I don't know is if I can access the insert value that made the insert fail.
For example:
table1(id,name, phone);
name is unique.
insert (1,a,123);
insert (2,a,1234);
What I want is when I do the second insert I to return the id value '1' without having to recur to a query.
Thank you in advance.
From oracle 10g r2 you can use log errors clause of insert command to log errors in a separate table. Here is an example:
SQL> create table test_table(
2 id number primary key,
3 col1 varchar2(7)
4 )
5 ;
Table created
-- creates a table for logging errors (table name will be prefaced with err$_)
SQL> begin dbms_errlog.create_error_log('TEST_TABLE'); end;
2 /
PL/SQL procedure successfully completed
-- violates primary key constraint
SQL> insert into test_table(id, col1)
2 ( select 1, level
3 from dual
4 connect by level <= 3)
5 log errors reject limit unlimited;
1 row inserted
SQL> commit;
SQL> select * from test_table;
ID COL1
---------- -------
1 1
SQL> select * from err$_test_table;
ORA_ERR_NUMBER$ ORA_ERR_MESG$ ORA_ERR_ROWID$ ORA_ERR_OPTYP$ ORA_ERR_TAG$ ID COL1
--------------- ------------------------------------------------------------------------------------------------------------
1 ORA-00001: unique constraint (HR.SYS_C008315) violated I 1 2
1 ORA-00001: unique constraint (HR.SYS_C008315) violated I 1 3
maybe you can write a trigger(before insert) on your table, on which insert about to happen. In this you can check if the column value(name) already exists in table.
In case it does you may insert this duplicate record in another table for further reference
Another approach is to write the insert in a procedure where the name may be checked and the duplicate name could be stored in a table.
Hope it helps

How to write an alter table statement to add a constraint

I'm, new to SQL. I have created few tables:
CREATE TABLE MAINTINANCE
(Maint_mname char(10),
Maint_date date,
Maint_duedate date NOT NULL,
Maint_mdesc char (15));
CREATE TABLE DESIGNERR
(Dez_emp_number varchar(11),
Dez_field char(12),
Dez_qualification char(10) NOT NULL,
Dez_experience smallint);
For the first table I am adding the following constraint:
ALTER TABLE MAINTINANCE ADD CONSTRAINT CHK_maintdate CHECK(Maint_date<MAint_duedate);
but I am getting the error invalid ALTER TABLE option. Could you please let me know why this is appearing? The same is working for a friend but not for me.
For the second table I have to write a SQL command for the business rule:
If the Qualification of a Designer is BS then a Minimum of 4 years
Experience is required. But, if the Qualification of the Designer is
MS then a Minimum of 2 years Experience is sufficient.
How can we define this business rule in SQL?
The code you posted works
SQL> CREATE TABLE MAINTINANCE
2 (Maint_mname char(10),
3 Maint_date date,
4 Maint_duedate date NOT NULL,
5 Maint_mdesc char (15));
Table created.
SQL> ALTER TABLE MAINTINANCE ADD CONSTRAINT CHK_maintdate CHECK(Maint_date<MAint_duedate);
Table altered.
If you're getting an error,
Cut and paste from a SQL*Plus session that shows exactly what statements you are executing
Post the full error stack
Although it does not affect your code, the word "MAINTINANCE" is misspelled. It should be "Maintenance". Future human developers will be grateful if your table names are spelled correctly.

SQLPlus AUTO_INCREMENT Error

When I try and run the following command in SQLPlus:
CREATE TABLE Hotel
(hotelNo NUMBER(4) NOT NULL AUTO_INCREMENT,
hotelName VARCHAR(20) NOT NULL,
city VARCHAR(50) NOT NULL,
CONSTRAINT hotelNo_pk PRIMARY KEY (hotelNo));
I get the following error:
(hotelNo NUMBER(4) NOT NULL AUTO_INCREMENT,
*
ERROR at line 2:
ORA-00907: missing right parenthesis
What am I doing wrong?
Many will gripe about this not being a standard feature in Oracle, but when it’s as easy as two more commands after your CREATE TABLE command I can’t see any good reason to use fancy SQL on every insert.
First let’s create a simple table to play with.
SQL> CREATE TABLE test
(id NUMBER PRIMARY KEY,
name VARCHAR2(30));
Table created.
Now we’ll assume we want ID to be an auto increment field. First we need a sequence to grab values from.
SQL> CREATE SEQUENCE test_sequence
START WITH 1
INCREMENT BY 1;
Sequence created.
Now we can use that sequence in a BEFORE INSERT trigger on the table.
CREATE OR REPLACE TRIGGER test_trigger
BEFORE INSERT
ON test
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT test_sequence.nextval INTO :NEW.ID FROM dual;
END;
/
SQL> INSERT INTO test (name) VALUES ('Jon');
1 row created.
SQL> INSERT INTO test (name) VALUES (’Bork’);
1 row created.
SQL> INSERT INTO test (name) VALUES (’Matt’);
1 row created.
SQL> SELECT * FROM test;
ID NAME
———- ——————————
1 Jon
2 Bork
3 Matt
Oracle has no auto_increment, you need to use sequences.
Or - starting with Oracle 12.1 - you can simply have:
CREATE TABLE employee
(
id NUMBER GENERATED by default on null as IDENTITY
....
)

Errors in my schema creation script

So I have a simple SQL script which creates a database schema of a simple library online catalog:
DROP TABLE book_copies;
/
DROP TABLE books_authors_xref;
/
DROP TABLE authors;
/
DROP TABLE books;
/
CREATE TABLE books (
isbn VARCHAR2(13) NOT NULL PRIMARY KEY,
title VARCHAR2(200),
summary VARCHAR2(2000),
date_published DATE,
page_count NUMBER
);
/
CREATE TABLE authors (
name VARCHAR2(200) NOT NULL PRIMARY KEY
);
/
CREATE TABLE books_authors_xref (
author_name VARCHAR2(200),
book_isbn VARCHAR2(13),
CONSTRAINT pk_books_authors_xref PRIMARY KEY (author_name, book_isbn),
CONSTRAINT fk_books_authors_xref1 FOREIGN KEY (author_name) REFERENCES authors (name),
CONSTRAINT fk_books_authors_xref2 FOREIGN KEY (book_isbn) REFERENCES books (isbn)
);
/
CREATE TABLE book_copies (
barcode_id VARCHAR2(100) NOT NULL PRIMARY KEY,
book_isbn VARCHAR2(13),
CONSTRAINT fk_book_copies FOREIGN KEY (book_isbn) REFERENCES books (isbn)
);
/
Whenever I run it through SQL*Plus, I get many errors during its execution even though it looks like all SQL orders execute properly. This is the output I get:
What does that mean? Am I doing something wrong?
The / in SQL*Plus executes the "command in the buffer". A statement terminated with a semicolon is executed and put into the buffer.
So the CREATE TABLE books .... is actually run twice. The first time because of the semicolon ; (which puts the statement into the buffer) and the second time when the parser hits the /.
That's why you get the "name is already used" error.
So you need to use either a semicolon or a slash, but not both.
Edit
You can see what's going on, when manually running a statement using both, in the following log I copied & pasted the first statement from your script to a SQL*Plus console:
SQL> DROP TABLE book_copies;
Table dropped.
SQL> /
DROP TABLE book_copies
*
ERROR at line 1:
ORA-00942: table or view does not exist
You can see clearly how the DROP TABLE is execute because of the semicolon, and how the / executes it again.