Different styles for assigning a primary key in oracle sql - sql

I was testing the following examples (1, 2 and 3) in APEX-Oracle SQL commands and all three examples seems to work fine, however, I thought the PRIMARY KEY is named/reserved keyword in oracle, so the question is there any difference between the primary keys in these three examples.
Please note that example_2 the primary key is in small letters
CREATE TABLE example_1(
ID int PRIMARY KEY,
LastName VARCHAR2(255) NOT NULL,
FirstName VARCHAR2(255),
Age int
);
CREATE TABLE example_2(
ID int primary key,
LastName VARCHAR2(255) NOT NULL,
FirstName VARCHAR2(255),
Age int
);
CREATE TABLE example_3(
ID int ,
LastName VARCHAR2(255) NOT NULL,
FirstName VARCHAR2(255),
Age int,
CONSTRAINT PK_example_3 PRIMARY KEY (ID)
);

No difference in functionality. Only difference is that for table example_3 the primary key is a named constraint and not a generated name.
"PRIMARY KEY" is not a reserved word (full list here) and case doesn't matter in the CREATE TABLE clause (object names can be case sensitive if enclosed in quotes)
You can easily check this yourself... query data dictionary USER_TABLES for details about the tables and USER_CONSTRAINTS to see if there are differences in the primary key constraints.

In Oracle INT is synonym for NUMBER(38) and that's bigger than a quad-precision integer. That's a big number that may be overkill for your app. I would recommend you use a smaller precision, like NUMBER(18) (a long) or so.
The first and second examples are equivalent. In the third case, you are assigning an explicit name (PK_EXAMPLE_3) to the primary key constraint. In the first two cases, the PK will end up with auto-generated "ugly" names. Not a big deal, but that name may be end up being different in different environments (dev, test, staging, prod) and that can be significant sometimes, when you submit SQL scripts to be executed by third parties, or in automated ways.
Other than that, your examples are almost equivalent.

As you noticed, letter case - in Oracle - doesn't matter. CREATE works as well as create or CrEAte. The same goes for primary key.
Letter case, though, would matter if you make a wrong step and decide to enclose names into double quotes; then you have to do that every time you reference that something (table, procedure, column, whatever), matching letter case exactly (but that's another issue we aren't discussing now).
You created table as
SQL> CREATE TABLE example_1(
2 ID int PRIMARY KEY,
3 LastName VARCHAR2(255) NOT NULL,
4 FirstName VARCHAR2(255),
5 Age int
6 );
Table created.
If you describe it, you'll notice that Oracle - by default - stores everything (into data dictionary) - in UPPER CASE and lets you reference it using any case you want:
SQL> desc example_1
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER(38)
LASTNAME NOT NULL VARCHAR2(255)
FIRSTNAME VARCHAR2(255)
AGE NUMBER(38)
SQL> select table_name from user_tables where table_name = 'EXAMPLE_1';
TABLE_NAME
--------------------
EXAMPLE_1
SQL> select id, LASTname, FirSTnaMe from exampLE_3;
no rows selected
SQL>
As you can see, your nicely written LastName became LASTNAME. As I said: you could have preserved it if you put it as "LastName" varchar2(255), but that's - in most cases - wrong and brings nothing but problems. Don't do that. Use e.g. last_name instead to improve readability.
As of constraints: as you didn't name them, Oracle set names automatically. These generic names aren't useful at all because you have no idea what they represent, simply by looking at their names:
SQL> select table_name, constraint_name, constraint_type
2 from user_constraints
3 where table_name = 'EXAMPLE_1';
TABLE_NAME CONSTRAINT_NAME CONSTRAINT_TYPE
-------------------- -------------------- --------------------
EXAMPLE_1 SYS_C009359 C
EXAMPLE_1 SYS_C009360 P
Yes, constraint type helps, but - why wouldn't you rather do as you did in your 3rd example and explicitly name the constraint?
SQL> select table_name, constraint_name, constraint_type
2 from user_constraints
3 where table_name = 'EXAMPLE_3';
TABLE_NAME CONSTRAINT_NAME CONSTRAINT_TYPE
-------------------- -------------------- --------------------
EXAMPLE_3 SYS_C009361 C
EXAMPLE_3 PK_EXAMPLE_3 P --> isn't this better?
SQL>
OK; you found two ways of creating (primary key) constraints:
one is inline (regardless of whether you give it a name or not):
id int primary key
another is out-of-line:
CONSTRAINT PK_example_3 PRIMARY KEY (ID)
the 3rd way is to use alter table statement:
SQL> CREATE TABLE example_2(
2 ID int,
3 LastName VARCHAR2(255) NOT NULL,
4 FirstName VARCHAR2(255),
5 Age int
6 );
Table created.
SQL> alter table example_2 add constraint pk_example_2 primary key (id);
Table altered.
I suggest you read carefully everything answerers said so far; you might find it useful.

Related

Getting an error of invalid identifier when try to create relationship between two tables in Oracle SQL developer? [duplicate]

This question already has an answer here:
A CREATE statement with quoted fields in Oracle
(1 answer)
Closed 1 year ago.
This is my device table
CREATE TABLE "DEVICE" (
"IMEI_Number" varchar(15),
"Device_Model" varchar(30),
"Device_Description" varchar(500),
"Assigned_Sim_Number" varchar(11),
"Activation_Date" timestamp,
"Deactivation_Date" timestamp,
"Manufacturer_ID" int,
"Customer_ID" int,
PRIMARY KEY ("IMEI_Number")
);
Then I have the manufacturer table which is
CREATE TABLE "MANUFACTURER" (
"Manufacturer_ID" int,
"Manufacturer_Name" varchar(30),
PRIMARY KEY ("Manufacturer_ID")
);
and I trying to create a relationship between these and getting the ORA-00904: "MANUFACTURER_ID": invalid identifier
My relationship code is
ALTER TABLE DEVICE
ADD FOREIGN KEY (Manufacturer_ID) REFERENCES MANUFACTURER(Manufacturer_ID);
That's just awful. Don't use double quotes when creating objects in Oracle as you'll have to use them every time you reference those objects, and match letter case every time.
SQL> alter table device add constraint fk_mf foreign key ("Manufacturer_ID")
2 references manufacturer ("Manufacturer_ID");
Table altered.
SQL>
A better option would be
SQL> create table device (
2 imei_number varchar2(15),
3 device_model varchar2(30),
4 device_description varchar2(500),
5 assigned_sim_number varchar2(11),
6 activation_date timestamp,
7 deactivation_date timestamp,
8 manufacturer_id int,
9 customer_id int,
10 primary key (imei_number)
11 );
Table created.
SQL> create table manufacturer (
2 manufacturer_id int,
3 manufacturer_name varchar2(30),
4 primary key (manufacturer_id)
5 );
Table created.
SQL> alter table device add constraint fk_mf foreign key (manufacturer_id)
2 references manufacturer (manufacturer_id);
Table altered.
SQL>
(Note also VARCHAR2 datatype; use that instead of VARCHAR).
By default, Oracle stores names in uppercase into data dictionary, but you can reference them using any case you want (upper, lower, mixed). If you do use double quotes, then you have to use their names exactly as during creation phase.
The columns in the tables you've created are surrounded by double-quotes, making their names case-sensitive, while the alter statement does not use quotes, and thus can't match the case-sensitive column name.
The best practice would be to drop these quotes and make the column names case-insensitive. If this is not an option, you could use the same quotes in the alter statement too:
ALTER TABLE DEVICE
ADD FOREIGN KEY ("Manufacturer_ID") REFERENCES MANUFACTURER("Manufacturer_ID");
-- Here ---------^---------------^--------------------------^---------------^

I am trying to VIEW 4 columns from 2 different tables I have already created in Oracle Live SQL

I want to use the VIEW command to display these 4 columns in one single schema. I have tried making a single VIEW with the first 3 columns, because they are from the the same table. Adding the one other column is where I'm struggling. I have tried the ALTER function but a VIEW schema doesn't seem to have the same edit privileges as a table would. I hope that makes sense.
create table PATIENTINFO (
PatientID number not null,
FirstName varchar2(50) not null,
LastName varchar2(50) not null,
Address varchar2(50),
City varchar2(50),
State varchar2(50),
ZipCode number(5),
Phone number(10) not null ,
Email varchar2(50),
MemberID number not null,
constraint pk_departments primary key (PatientID)
)
create table LABORDER (
LabOrderNumber number not null,
OrDate date not null,
ReqBloodTest varchar2(15) not null,
Reason varchar(50),
PatientID number not null,
constraint pk_laborder primary key (LabOrderNumber),
constraint fk_laborder_patientid foreign key (PatientID)
references PATIENTINFO (PatientID)
)
CREATE VIEW PatientBlood AS
SELECT FirstName, LastName, PatientID
FROM PATIENTINFO
Write the query you want and then create a view out of it. I started by writing the query below and then prefixed it with CREATE OR REPLACE VIEW. The example below has some randomly selected columns, change it to whatever columns you need. I chose to name my columns in the view definition, you can omit that but also do a million other things as stated in the docs
Side note: don't use mixed case for identifiers like column names/table names. It is confusing. In your case it didn't matter since you didn't use quotes, so they're case insensitive and the view below will work even though the identifiers are all lower case.
CREATE OR REPLACE VIEW laborder_v (
labordernumber,
patientid,
lastname
) AS
SELECT o.labordernumber,
p.patientid,
p.lastname
FROM laborder o
JOIN patientinfo p ON o.patientid = p.patientid;

How to update rows with the same foreign key without updating them all

I'm working on my university project and I need some help about Oracle database.
I have two tables USERFORM and ACADEMICTRAINING.
USERFORM
Name Null? Type
-------------- -------- ------------
USER_ID NOT NULL NUMBER(5)
FIRSTNAME NOT NULL VARCHAR2(30)
LASTNAME NOT NULL VARCHAR2(30)
EMAIL NOT NULL VARCHAR2(60)
BORN_DATE NOT NULL DATE
PHONE NOT NULL VARCHAR2(20)
ACCESSPASSWORD NOT NULL VARCHAR2(30)
ACADEMICTRAINING
Name Null? Type
-------------- -------- -------------
AT_ID NOT NULL NUMBER(5)
START_DATE NOT NULL DATE
END_DATE NOT NULL DATE
INSTITUTION NOT NULL VARCHAR2(100)
COURSE NOT NULL VARCHAR2(70)
AT_DESCRIPTION VARCHAR2(200)
AT_ID is a foreign key that references USERFORM on USER_ID column.
I would like to know if there's some way to UPDATE one single row without affecting other rows with the same foreign key.
Your data structure is messed up, which is what is confusing you. Why are you naming a user column AT_ID. Also the foreign keys are not declared. A better approach is:
CREATE TABLE ACADEMICTRAINING (
USER_ID NUMBER(5) NOT NULL FOREIGN KEY REFERENCES USERFORM(USER_ID),
START_DATE DATE NOT NULL,
END_DATE DATE NOT NULL
INSTITUTION VARCHAR2(100) NOT NULL
COURSE VARCHAR2(70) NOT NULL
AT_DESCRIPTION VARCHAR2(200)
);
You really want to have a primary key in the table. Under most circumstances, I would recommend an auto-incrementing primary key. But that is hard to do automatically in Oracle prior to Oracle 12. Such a column should be called something like ACADEMICTRAINING_ID.
Instead, you really want a unique key. I don't know what fits your requirements, but let's say the first two columns cannot be repeated (more columns might be involved). Then:
ALTER TABLE ACADEMICTRAINING ADD CONSTRAINT UNQ_ACADEMICTRAINING_2
UNIQUE (USER_ID, START_DATE);
All that said, what you need in your UPDATE is a way to choose which row. Something like this:
UPDATE ACADEMICTRAINING
SET . . .
WHERE USER_ID = :USER_ID AND START_DATE = :START_DATE;
If you had a primary key, it would look like:
UPDATE ACADEMICTRAINING
SET . . .
WHERE ACADEMICTRAINING_ID = :ACADEMICTRAINING_ID;
Try this.
Update ACADEMICTRAINING
set COURSE = 'BSC.CS'
where AT_ID = (select USER_ID
from USERFORM
where FIRSTNAME = 'Ali'
and LASTNAME = 'Ahmed'
and PHONE = '44425183658558' );

SQL Integrity constraint-Parent key not found

I have looked over the internet and their solutions wont fix my problem, hence I'm asking for help here to check if there's mistakes in my coding.
I wanted to create a temporary table populated by other source tables and then implement it into the fact table. I have checked if the data type and the parameter is matching or the sequence of the keys but still it's giving me the error
"ORA-02291: integrity constraint (SYSTEM.SYS_C007167) violated -
parent key not found"
Fact Table:
CREATE TABLE DW_ITEMS7364 (
DW_ID int not null,
ManID char(5),
WHID char(5),
STKID char(5),
Profit number,
CONSTRAINT DW_ID PRIMARY KEY (DW_ID),
FOREIGN KEY(ManID) REFERENCES DW_MANUFACTURER7364,
FOREIGN KEY(WHID) REFERENCES DW_WAREHOUSE7364,
FOREIGN KEY(StkID) REFERENCES DW_STOCKITEM7364);
CREATE SEQUENCE seq_items7364 START WITH 101 increment by 1;
CREATE TRIGGER trg_items7364 BEFORE INSERT OR UPDATE ON DW_ITEMS7364
FOR EACH ROW
BEGIN
SELECT seq_items7364.NEXTVAL
INTO :new.DW_ID
FROM dual;
END;
Temporary Table:
CREATE TABLE TEMP_TAB7364 AS( SELECT m.ManID, w.WHID, s.STKID, (s.SellingPrice-s.PurchasePrice) AS "Profit"
FROM MANUFACTURER7364 m LEFT OUTER JOIN STOCKITEM7364 s ON s.ManID = m.ManID
RIGHT OUTER JOIN WAREHOUSE7364 w on s.WHID = w.WHID WHERE s.SELLINGPRICE IS NOT NULL AND s.PURCHASEPRICE IS NOT NULL
);
These are my source tables:
CREATE TABLE MANUFACTURER7364(
ManID char(5),
ManName varchar (25),
CityID char(5) NOT NULL,
PRIMARY KEY(ManID),
FOREIGN KEY(CityID) REFERENCES CITY7364);
CREATE TABLE WAREHOUSE7364(
WHID char(5),
MaxNoOfPallets number,
CostPerPallet number,
SecurityLevel char(1),
FreezerFacilities varchar(10),
QuarantineFacilities varchar(10),
CityID char(5) NOT NULL,
PRIMARY KEY(WHID),
FOREIGN KEY(CityID) REFERENCES CITY7364);
CREATE TABLE STOCKITEM7364(
StkID char(5),
StkName varchar(20),
SellingPrice number,
PurchasePrice number,
ManID char(5) NOT NULL,
WHID char(5) NOT NULL,
PRIMARY KEY(StkID),
FOREIGN KEY(ManID) REFERENCES MANUFACTURER7364,
FOREIGN KEY(WHID) REFERENCES WAREHOUSE7364);
As far as I can tell, nothing of what you posted raises that error.
Additional drawback is the way you chose to create foreign key constraints. If you don't name it, Oracle assigns the name itself and it looks the way you posted it: SYSTEM.SYS_C007167.
SQL> create table test
2 (id_dept number,
3 id_emp number,
4 foreign key (id_dept) references dept (deptno),
5 foreign key (id_emp) references emp (empno));
Table created.
SQL> select constraint_name from user_constraints where table_name = 'TEST';
CONSTRAINT_NAME
------------------------------
SYS_C008172
SYS_C008173
SQL>
When one of these fails, looking at its name you have no idea what went wrong, unless you investigate a little bit more:
SQL> select column_name from user_cons_columns where constraint_name = 'SYS_C008173';
COLUMN_NAME
-----------------------
ID_EMP
SQL>
But, if you name the constraint, it is way simpler:
SQL> create table test
2 (id_dept number,
3 id_emp number,
4 constraint fk_test_dept foreign key (id_dept) references dept (deptno),
5 constraint fk_test_emp foreign key (id_emp) references emp (empno));
Table created.
SQL> select constraint_name from user_constraints where table_name = 'TEST';
CONSTRAINT_NAME
------------------------------
FK_TEST_DEPT
FK_TEST_EMP
SQL>
Another major drawback one notices is what's written in front of the dot, here: SYSTEM.SYS_C007167. Yes, that would be SYSTEM. Shortly, don't do that. Leave SYS and SYSTEM alone; they are powerful, they are special. Why would you take the risk of destroying the database if you (un)intentionally do something hazardous? Create another user, grant required privileges and work in that schema.
If I understood you correctly, once you create that temp table (TEMP_TAB7364), its contents is transferred into the DW_ITEMS7364 and - doing so - you hit the error.
If that's so, what's the purpose of the temp table? Insert directly into the target table and save resources. Will it fail? Of course it will, unless you change the query. How? I don't know - make sure that you don't insert values that don't exist in any of three tables used for enforcing referential integrity.
Though, as you already have the temp table, if it isn't too large, a (relatively) quick & dirty way of finding out which row is responsible for the error can be found with a loop, such as
begin
for cur_r in (select col1, col2, ... from temp_table) loop
begin
insert into target (col1, col2, ...)
values (cur_r.col1, cur_r.col2, ...);
exception
when others then
dbms_output.put_line(sqlerrm ||': '|| cur_r.col1 ||', '||cur_r.col2);
end;
end loop;
end;
The inner BEGIN-END block is here to make sure that the PL/SQL code won't exit at the first error, but will display them all. Then review those values and find the reason that makes your query invalid.

Is there any way to reference column with different datatype?

I have 2 schemas/tables as shown:
CREATE TABLE schema1.code_tbl
( code CHAR(6) PRIMARY KEY,
description CHAR(30)
);
CREATE TABLE schema2.record_tbl
( rec_id VARCHAR(10) PRIMARY KEY,
curr_code VARCHAR(6),
remarks VARCHAR(30)
);
I need to create a foreign key reference from curr_code in RECORD_TBL to code in CODE_TBL.
ALTER TABLE schema2.record_tbl
ADD CONSTRAINT record_code_fk
FOREIGN KEY (curr_code)
REFERENCES schema1.code_tbl (code);
This obviously gives me an ORA-02267 (column type incompatible with referenced column) error.
I cannot alter the code column in CODE_TBL because I do not own or control schema1. I cannot alter the curr_code column in RECORD_TBL because it would break many functions in my application because we don't account for trailing whitespaces.
Is there any other way to enforce referential integrity between the 2 columns?
If schema2 is on Oracle 11g and above, using virtual column may solve your problem, but that will change the structure of your table which you are trying to avoid. If you can manage it, here is how it can be done
SQL> CREATE TABLE code_tbl
2 ( code CHAR(6) PRIMARY KEY,
3 description CHAR(30)
4 );
Table created
SQL>
SQL> CREATE TABLE record_tbl
2 ( rec_id VARCHAR2(10) PRIMARY KEY,
3 curr_code VARCHAR2(6),
4 remarks VARCHAR2(30)
5 );
Table created
SQL> INSERT INTO code_tbl(code, description) VALUES ('ABC', 'Test Data');
1 row inserted
SQL> INSERT INTO record_tbl(rec_id, curr_code, remarks) VALUES ('1', 'ABC', 'Test Row');
1 row inserted
SQL> SELECT * FROM record_tbl;
REC_ID CURR_CODE REMARKS
---------- --------- ------------------------------
1 ABC Test Row
SQL> SELECT * FROM code_tbl;
CODE DESCRIPTION
------ ------------------------------
ABC Test Data
SQL> ALTER TABLE record_tbl ADD curr_code_v CHAR(6) AS (trim(curr_code));
Table altered
SQL> SELECT * FROM record_tbl;
REC_ID CURR_CODE REMARKS CURR_CODE_V
---------- --------- ------------------------------ -----------
1 ABC Test Row ABC
SQL>
SQL> ALTER TABLE record_tbl
2 ADD CONSTRAINT record_code_fk
3 FOREIGN KEY (curr_code_v)
4 REFERENCES code_tbl (CODE);
Table altered
SQL> INSERT INTO record_tbl(rec_id, curr_code, remarks) VALUES ('2', 'ABC', 'Test Row 2');
1 row inserted
SQL> INSERT INTO record_tbl(rec_id, curr_code, remarks) VALUES ('3', 'XYZ', 'Test Row 2');
INSERT INTO record_tbl(rec_id, curr_code, remarks) VALUES ('3', 'XYZ', 'Test Row 2')
ORA-02291: integrity constraint (USER_X.RECORD_CODE_FK) violated - parent key not found
Here are the words from Tom Kyte regarding virtual columns:
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:676611400346196844
So the situation is this. You have an existing table record_tbl which is well established ( obviously, because changing it "break many functions"). Belatedly somebody has decided to enforce relational integrity on this table but has chosen to do so referencing a table in a different schema with a column of a different datatype.
Hmmmm.
Your options are:
Do nothing. Always an option; your application has survived with the current state for some length of time, maybe you can continue to live with the accrued technical debt.
Refactor one of the schemas. If you need to enforce the foreign key - and let's face it, relational integrity is a good thing - then you are going to have to change the datatype of one of the columns. Which one you choose is a project decision: changing a column in a schema you don't own is a political problem (initially), and political problems are usually harder than technical problems. Refactoring a schema by changing a column type is a matter of testing, testing, testing.
Replication. Build a materialized view in schema2 which copies the data from schema1.code_tbl. Crucially, define the MView code column match the datatype of schema2.record_tbl.curr_code i.e. varchar2(6). You will now be able to enforce a foreign key against schema2.mv_code_tbl.code. Note: the data in the MView column will be formatted as CHAR i.e. with trailing spaces.
You can not create relation in different type column.
Why you must create relation in database? You can connect the table using only the code.