Referencing a non-PK field in Oracle - database

What is the correct way to reference a non-PK field? I don't understand why this example doesn't work (the field c23).
CREATE TABLE tabla1 (
c11 CHAR(9) PRIMARY KEY,
c12 VARCHAR2(10),
c13 INTEGER,
CHECK(c13 <= 100)
);
CREATE TABLE tabla2 (
c21 CHAR(9) PRIMARY KEY,
c22 VARCHAR2(10),
c23 REFERENCES tabla1(c13)
);

You can't. If you want to create a foreign key constraint, you have to reference a primary or unique key column. Any other column won't work.
For example:
SQL> CREATE TABLE tabla1 (
2 c11 CHAR(9) PRIMARY KEY,
3 c12 VARCHAR2(10),
4 c13 INTEGER UNIQUE,
5 CHECK(c13 <= 100)
6 );
Table created.
SQL> CREATE TABLE tabla2 (
2 c21 CHAR(9) PRIMARY KEY REFERENCES tabla1 (c11),
3 c22 VARCHAR2(10),
4 c23 INTEGER REFERENCES tabla1(c13)
5 );
Table created.
SQL>

Related

Error when inserting record into a database Oracle SQLPLUS

I am new to this, so this can be silly, but..
I have 3 created tables:
create table faculty (id_faculty number(2) Primary Key, //id , 2 digits, primary key
faculty_name varchar2(30) constraint f_name Not Null, // name, up to 30 symbols, not null
dean varchar2(20), // dean, up to 20 symbols
telephone varchar2(8)); //number, up to 8 symbols
create table student (id_student number(3) Primary Key, //id, 3 digits, primary key
student_name varchar(20) constraint s_name Not Null, // name, up to 20 symbols, not null
student_surname varchar(20) constraint s_surname Not Null, // name, up to 20 symbols, not null
course_year number(1) constraint s_course_year Check(course_year>0 and course_year<=6), // number, 1 digit, in range 1-6
faculty_id number(2), // id, 2 digits
constraint s_fkey Foreign key (Faculty_ID) References faculty(ID_faculty)); // foreign key to table faculty to id_faculty
create table money (id_payout number(4) Primary Key,
student_id number(3),
constraint m_fkey Foreign key (student_ID) References student(ID_student),
payout_date date constraint p_date Not Null,
stipend number(5,2) Check(stipend<151),
compensation number(5,2));
Then I created view:
create or replace view stud_stip AS
Select f.faculty_name, student_surname, student_name, s.course_year,
m.stipend, m.payout_date
from
faculty f inner join student s on f.id_faculty=s.faculty_id
inner join money m on m.student_id=s.id_student
where
m.payout_date = ( select distinct max(payout_date)
from money
where money.student_id=money.student_id)
with check option;
This is how the view looks
Then I wanted to add information to a view:
insert into stud_stip values ('Partikas tehnologiju', 'Lapa', 'Jana', 2, 120, '03.12.1998');
This is the error I got:
ORA-01779: cannot modify a column which maps to a non key-preserved table
I have googled all over the internet and did not solve my error, please don't send me other topics about this question, because I probably did read it.
I will be very grateful for the answers. Thank you in advance.
The way to do it is to create an instead of trigger on a view which would - in turn - insert rows into appropriate tables.
As all ID columns are primary keys and you didn't explain how you populate them, I'll do it using a sequence, in the same instead of trigger.
SQL> create sequence seqs;
Sequence created.
SQL> create or replace trigger trg_stud_stip
2 instead of insert on stud_stip
3 for each row
4 begin
5 insert into faculty (id_faculty, faculty_name)
6 values (seqs.nextval, :new.faculty_name);
7
8 insert into student (id_student, student_name, student_surname, course_year)
9 values (seqs.nextval, :new.student_name, :new.student_surname, :new.course_year);
10
11 insert into money (id_payout, stipend, payout_date)
12 values (seqs.nextval, :new.stipend, :new.payout_date);
13 end;
14 /
Trigger created.
Testing (don't insert strings into DATE datatype columns!):
SQL> insert into stud_stip
2 (faculty_name, student_surname, student_name, course_year, stipend, payout_date)
3 values
4 ('Partikas tehnologiju', 'Lapa', 'Jana', 2, 120, date '1998-12-03');
1 row created.
SQL> select * from faculty;
ID_FACULTY FACULTY_NAME DEAN TELEPHON
---------- ------------------------------ -------------------- --------
1 Partikas tehnologiju
SQL> select * from student;
ID_STUDENT STUDENT_NAME STUDENT_SURNAME COURSE_YEAR FACULTY_ID
---------- -------------------- -------------------- ----------- ----------
2 Jana Lapa 2
SQL> select * from money;
ID_PAYOUT STUDENT_ID PAYOUT_DAT STIPEND COMPENSATION
---------- ---------- ---------- ---------- ------------
3 03.12.1998 120
SQL>
It works. Now that you know how, improve it if necessary.

How do I insert a value from a table into another table in the same database? And only take out the year from date?

E.g.
CREATE TABLE A(
YEAR NUMBER(4) NOT NULL,
LENGTH NUMBER(5,2) NOT NULL,
CONSTRAINT AVGYEAR_PKEY PRIMARY KEY(YEAR))
CREATE TABLE B(
ID1 NUMBER(10) NOT NULL,
DATE_DONE DATE NOT NULL,
CONSTRAINT TRIP_PKEY PRIMARY KEY (ID1),
);
I want to insert values into table A, but the YEAR value from Table A,I would have to take from table b and only the 'YYYY' not the whole date.
Do i do something like this
insert into A VALUES(YEAR value,LENGTH value)
insert into A VALUES(select TO_CHAR((DATE_DONE),'YYYY') from B, 10.5);
If I understand well, this could be what you need.
Say you have
SQL> select * from B;
ID1 DATE_DONE
---------- ---------
1 01-JAN-19
2 31-DEC-18
3 01-JUN-17
SQL> select * from A;
no rows selected
with this
SQL> insert into a(year, length) select extract(year from date_done), 10 from B;
3 rows created.
you get:
SQL> select * from A;
YEAR LENGTH
---------- ----------
2019 10
2018 10
2017 10
If you need a distinct, this is the way:
insert into a(year, length) select DISTINCT extract(year from date_done), 10 from B;

Possible duplicates to find in SQL

I've been trying the following suggestions from this post, but this does not apply for my case or at least I am not capable of adapting the query to my needs.
I have three tables: one stands for documents header, one stands for documents lines and one stands for item's information (Code, Description etc).
I would like to extract all the documents number that have the same value (this is the information from the documents header table), the same items (the code of the item) and the same quantity (from the lines of the documents tables). How can I extract this information? Thanks
The tables are -
DocHeader DocLines Items
ID fDocID ID
Code fItemID Code
Date Quantity Description
---- -------- -----------
TotalValue etc etc
Later edit
Output should like something like:
DocCode ItemCode Quantity TotalValue
01 001 5 1000
01 002 5 1000
01 003 4 1000
02 001 5 1000
02 002 5 1000
02 003 4 1000
DDL
create table DocHeader
(
Id bigint not null identity(1,1) primary key clustered
, Code nvarchar(32) not null
, [Date] datetime not null
)
go
create table Items
(
Id bigint not null identity(1,1) primary key clustered
, Code nvarchar(32) not null
, [Description] nvarchar(256)
, UnitPrice money not null
)
go
create table DocLines
(
Id bigint not null identity(1,1) primary key clustered
,fDocId bigint not null constraint fk_DocLines_fDocId foreign key references DocHeader(Id)
,fItemId bigint not null constraint fk_DocLines_fDocId foreign key references Items(Id)
,Quantity int not null
)
go
create view vDocHeader as
select dh.*
, x.TotalValue
from DocHeader
left outer join
(
select dl.fDocId
, sum(dl.Quantity * i.UnitPrice) TotalValue
from DocLines dl
inner join Items i
on i.Id = dl.fItemId
group by dl.fDocId
) x
on x.fDocId = dh.Id
Why not simple grouping?
SELECT dh.Code AS DocCode, i.Code AS ItemCode, Quantity, SUM(dl.Quantity * i.UnitPrice) AS TotalValue
FROM DocHeader dh
LEFT JOIN DocLines dl ON dl.fDocId=dh.id
JOIN Items i ON i.Id = dl.fItemId
GROUP BY dh.Code,i.Code,Quantity

Unique values in two columns

I have two columns and I need them both to be unique between each other (like it is 1 column).
My first attempt was to create sequence and set default constraints.
create sequence seq1
as bigint
start with 1
increment by 1
cache;
go
create table product (
pk uniqueidentifier
, id_1 bigint not null default (next value for seq1)
, id_2 bigint not null default (next value for seq1)
);
go
insert into product (pk) values (newid());
insert into product (pk) values (newid());
insert into product (pk) values (newid());
insert into product (pk) values (newid());
insert into product (pk) values (newid());
go
And it doesn't work. The result of query above will be:
id_1 id_2
1 1
2 2
3 3
4 4
5 5
Currently I stopped using 2 sequences with even and not even numbers.
create sequence seq2
as bigint
start with 1
increment by 2
cache;
go
create sequence seq3
as bigint
start with 2
increment by 2
cache;
go
But if in future I will need to add another column which also must be unique I will have a problem.
I also thinked about stored procedures. Something like this works for me.
create procedure sp_insertProduct
as
begin
declare #id1 as bigint = next value for seq1;
declare #id2 as bigint = next value for seq1;
insert into product (pk, id_1, id_2) values (newid(), #id1, #id2);
end
go
exec sp_insertProduct;
exec sp_insertProduct;
exec sp_insertProduct;
go
But due to my ORM framework restictions I cannot use stored procedures for inserting.
So is there a better solution for that problem?
PS. for some reasons I can not use uniqueidentifiers.
UPDATE
I think I need to explain question a bit clearly. I have a working solution for now (and both current answers will also work), but I wonder if there is a extensible solution to:
provide uniqueness of values in multiple columns (with ability to
add additional columns in future).
avoid using uniqueidentifiers
for better understanding of question, this is how I check uniqueness:
with src as (
select id_1 as id from product
union all
select id_2 as id from product
)
select id, count(*) as equal_values
from src
group by id
having (count(*) > 1)
create sequence seq1
as bigint
start with 1
increment by 2
cache;
go
create table product (
pk uniqueidentifier
, id_1 bigint not null default (next value for seq1)
, id_2 bigint not null default (next value for seq1 + 1)
);
go
doing this..
insert into product (pk) values (newid());
insert into product (pk) values (newid());
insert into product (pk) values (newid());
insert into product (pk) values (newid());
insert into product (pk) values (newid());
this would generate..
pk id_1 id_2
2A159914-8105-4DC1-9D7E-570CC5444172 1 2
6DAFEF16-2B81-4A10-99EF-B3F1A74389C6 3 4
8C6F6697-D993-4320-92BB-04CD56804C5A 5 6
AC97F37F-CAC3-4E83-BDD4-4B55D009C334 7 8
3DDAADA0-D7DB-4350-8087-ABF02B539552 9 10
SQL Fiddle
May be this seems very naive but it should work. I didn't check but you may need to add some parenthesis:
create table product (
pk uniqueidentifier
, id_1 bigint not null default (next value for seq1)
, id_2 bigint not null default (-next value for seq1)
);
go

Oracle: Finding Primary Keys - PK and NN

I am working with a database and was told the Table USERS has a single Primary Key - USERID. Using ALL_CONS_COLS gives me the following:
OWNER | TABLE_NAME | CONSTRAINT_NAME | COLUMN_NAME | POSITION
----------------------------------------------------------------
MONSTER | USERS | USER_ID_PK | USERREF | 2
MONSTER | USERS | USER_ID_NN | USERID
MONSTER | USERS | USER_ID_PK | USERID | 1
This suggests the PK is a composite comprised of: USERREF and USERID.
There are a few things I do not follow:
Why does USERID have both a NOT NULL constraint and also a PK constraint? PK by definition means NN.
Secondly, as USERID has a NN constraint, why does USERREF not have a NN constraint?
Thirdly, why is CONSTRAINT_NAME "suggesting" the PK is USER_ID (USERID), i.e., why is the constraint named after a single column; wouldn't USERS_PK be a better constraint name?
So, ultimately, is it a Composite PK or not?
So, ultimately, is it a Composite PK or not?
Yes. The primary key is defined on two columns together. The position will tell you the leading column. USER_ID_PK primary key constraint is defined on USERID as leading column followed by USERREF.
Why does USERID have both a NOT NULL constraint and also a PK constraint?
Because someone created a NOT NULL constraint on the primary key column explicitly.
Let's test and see.
Single primary key
SQL> create table t(a number primary key);
Table created.
SQL> SELECT a.table_name,
2 b.column_name,
3 a.constraint_type,
4 b.position
5 FROM user_constraints a
6 JOIN user_cons_columns b
7 ON a.owner = b.owner
8 AND a.constraint_name = b.constraint_name
9 AND a.table_name = b.table_name
10 AND a.constraint_type IN ('P', 'C');
TABLE_NAME COLUMN_NAM C POSITION
---------- ---------- - ----------
T A P 1
Composite primary key
SQL> drop table t purge;
Table dropped.
SQL> create table t(a number, b number);
Table created.
SQL> alter table t add constraint t_pk PRIMARY KEY(a,
Table altered.
SQL> SELECT a.table_name,
2 b.column_name,
3 a.constraint_type,
4 b.position
5 FROM user_constraints a
6 JOIN user_cons_columns b
7 ON a.owner = b.owner
8 AND a.constraint_name = b.constraint_name
9 AND a.table_name = b.table_name
10 AND a.constraint_type IN ('P', 'C');
TABLE_NAME COLUMN_NAM C POSITION
---------- ---------- - ----------
T A P 1
T B P 2
Primary key and NOT NULL
SQL> drop table t purge;
Table dropped.
SQL> create table t(a number primary key not null);
Table created.
SQL> SELECT a.table_name,
2 b.column_name,
3 a.constraint_type,
4 b.position
5 FROM user_constraints a
6 JOIN user_cons_columns b
7 ON a.owner = b.owner
8 AND a.constraint_name = b.constraint_name
9 AND a.table_name = b.table_name
10 AND a.constraint_type IN ('P', 'C');
TABLE_NAME COLUMN_NAM C POSITION
---------- ---------- - ----------
T A C
T A P 1

Resources