When I create the table EMPLOYEE, I made ESSN as a primary key and the SUPERSSN a foreign key from the same table with DNO as a foreign key from the dep table, when I want to insert values that show up and now I am confused.
The table contains the following:
Name Null? Type
----------------------------------------- -------- --------------
ENAME NOT NULL VARCHAR2(30)
ESSN NOT NULL CHAR(14)
BDATE DATE
DNO NUMBER(38)
SUPERSSN CHAR(14)
in first time I used the following command line:
INSERT INTO EMPLOYEE ('JOSEPH','789456','14-DEC-1986','3','123');
then I try without DNO as that:
SQL> INSERT INTO EMPLOYEE (ENAME,ESSN,BDATE)
2 VALUES('JOSEPH','9861050560','14-DEC-1986');
-------------------------------
INSERT INTO EMPLOYEE (ENAME,ESSN,BDATE)
*
ERROR at line 1:
ORA-02291: integrity constraint (SYSTEM.SYS_C007150) violated - parent key not
found
----------------------------
Most likely parent_key record which is SUPERSSN(assuming) must be missing in parent table. You can find that out by
SELECT *
FROM user_constraints
WHERE table_name = 'EMPLOYEE'
So you need to first insert values in parent table of employees table and then insert values in child table.
To find out parent_table do as
SELECT uc.constraint_name
|| CHR (10)
|| '('
|| ucc1.TABLE_NAME
|| '.'
|| ucc1.column_name
|| ')'
constraint_source,
'REFERENCES'
|| CHR (10)
|| '('
|| ucc2.TABLE_NAME
|| '.'
|| ucc2.column_name
|| ')'
references_column
FROM user_constraints uc, user_cons_columns ucc1, user_cons_columns ucc2
WHERE uc.constraint_name = ucc1.constraint_name
AND uc.r_constraint_name = ucc2.constraint_name
AND ucc1.POSITION = ucc2.POSITION
AND UC.TABLE_NAME = 'EMPLOYEE'
AND uc.constraint_type = 'R'
For more details please have a look at this.
And go through this and this as well.
I think the new entry(having foreign key constraint) you are entering is referring to an entry which doesnt exist
Here is a query you could use to check to see if the values in your foreign key table exist or not, and of course if they do not exist then they would have to be inserted to resolve the parent key violation:
SELECT E.* FROM EMPLOYEE E
LEFT JOIN SUPER_TABLE S
ON E.SUPERSSN = S.SUPERSSN
WHERE S.SUPERSSN IS NULL AND E.SUPERSSN IS NOT NULL;
Of course, that is assuming the problem is with the SUPERSSN key. If you have other foreign keys, then you may have to check those as well.
Related
I have some code (see below), which uses a hierarchical query that traverses down a table to find all the related Foriegn keys. This seems to be working fine.
Can this be modified not to display the top level table, in this case the TABLE_NAME='PARENT' as I want to wrap this code in PL/SQL in order to create the desired syntax to enable/disable Foriegn keys for all the descendants of a table.
In my example, I have 3 tables. A grandchild table, which REFERENCES a child table and a child table, which REFERENCES the parent table, which is the master table.
The reason for this is to clean up some massive tables and I am trying to figure out the most effective and efficient way to go about this exercise.
We don't have CASCADE DELETE setup as that is too dangerous and some people learned that the hard way unfortunately.
Below is my test case and expected results, which I plan to execute in a procedure.
create table parent (
id NUMBER(10),
value varchar2(30),
constraint parent_pk primary key (id)
);
CREATE TABLE child
( id NUMBER(10) not null,
value NUMBER(10) not null,
constraint child_pk primary key (id,value),
CONSTRAINT parent_child_fk
FOREIGN KEY (id)
REFERENCES parent(id));
CREATE TABLE grandchild
( id NUMBER(10) not null,
value NUMBER(10) not null,
constraint grandchild_pk primary key (id,value),
CONSTRAINT child_grandchild_fk
FOREIGN KEY (id,value)
REFERENCES child(id,value));
insert into parent values (1,'a');
insert into parent values (2,'b');
insert into parent values (3,'c');
insert into child values (1,1);
insert into child values (1,2);
insert into child values (1,3);
insert into child values (2,1);
insert into child values (2,2);
insert into child values (2,3);
insert into child values (3,1);
insert into child values (3,2);
insert into child values (3,3);
insert into grandchild values (1,1);
insert into grandchild values (1,2);
insert into grandchild values (1,3);
insert into grandchild values (2,1);
insert into grandchild values (2,2);
insert into grandchild values (2,3);
insert into grandchild values (3,1);
insert into grandchild values (3,2);
insert into grandchild values (3,3);
In the query I hard code the column name ID.
If possible, I would like to figure it out else I can live with the hard coded value.
select distinct table_name, constraint_name, column_name, r_table_name, position, constraint_type
from (
SELECT uc.table_name,
uc.constraint_name,
cols.column_name,
(select table_name from user_constraints where constraint_name = uc.r_constraint_name)
r_table_name,
(select column_name from user_cons_columns where constraint_name = uc.r_constraint_name and position = cols.position)
r_column_name,
cols.position,
uc.constraint_type
FROM user_constraints uc
inner join user_cons_columns cols on uc.constraint_name = cols.constraint_name
where constraint_type != 'C'
)
start with table_name = 'PARENT' and column_name = 'ID'
connect by nocycle
prior table_name = r_table_name
and prior column_name = r_column_name;
TABLE_NAME
CONSTRAINT_NAME
COLUMN_NAME
R_TABLE_NAME
POSITION
CONSTRAINT_TYPE
PARENT
PARENT_PK
ID
-
1
P
GRANDCHILD
CHILD_GRANDCHILD_FK
ID
CHILD
1
R
CHILD
PARENT_CHILD_FK
ID
PARENT
1
R
Expected results for my test CASE is to generate this syntax:
alter table CHILD disable constraint PARENT_CHILD_FK;
alter table GRANDCHILD disable constraint CHILD_GRANDCHILD_FK;
You can use:
SELECT 'ALTER TABLE "' || u.owner || '"."' || u.table_name || '" '
|| 'DISABLE CONSTRAINT "' || u.constraint_name || '"' AS statement
FROM user_constraints u
INNER JOIN user_constraints r
ON ( u.constraint_type = 'R'
AND r.constraint_type IN ('P', 'U')
AND u.r_owner = r.owner
AND u.r_constraint_name = r.constraint_name)
START WITH r.table_name = 'PARENT'
CONNECT BY PRIOR u.owner = r.owner
AND PRIOR u.table_name = r.table_name;
db<>fiddle here
It is not necessary to use hard code, in fact, it is more troublesome to display the top-level table
with tab1 as (
select t1.CONSTRAINT_NAME,
t1.CONSTRAINT_TYPE,
t1.TABLE_NAME,
t1.R_CONSTRAINT_NAME,
t2.CONSTRAINT_TYPE r_type,
t2.TABLE_NAME r_name
from user_constraints t1,
user_constraints t2
where t1.CONSTRAINT_TYPE = 'R'
and t1.R_CONSTRAINT_NAME = t2.CONSTRAINT_NAME
)
, tab2 as(
select t1.*
from tab1 t1
start with not exists(
select 1 from tab1 v1 where t1.r_name = v1.table_name
)
connect by prior t1.table_name = t1.r_name
)
select 'alter table ' || t1.table_name || ' disable constraint ' || t1.CONSTRAINT_NAME || ';'
from tab2 t1
;
Let say I have these 3 constraint given:
ALTER TABLE actor ADD CONSTRAINT PK_ACTORID PRIMARY KEY (actor_id);
ALTER TABLE film ADD CONSTRAINT PK_FILMID PRIMARY KEY (film_id);
ALTER TABLE film_actor ADD CONSTRAINT FK_FILMID1 FOREIGN KEY (film_id) REFERENCES film;
I need to write sql to show these table constraints:
-- Check which constraints added in ACTOR table
SELECT OWNER, CONSTRAINT_NAME, TABLE_NAME, SEARCH_CONDITION, INDEX_NAME
FROM USER_CONSTRAINTS
WHERE TABLE_NAME = 'ACTOR';
-- Check which constraints added in FILM_ACTOR table
SELECT OWNER, CONSTRAINT_NAME, TABLE_NAME, SEARCH_CONDITION, INDEX_NAME
FROM USER_CONSTRAINTS
WHERE TABLE_NAME = 'FILM_ACTOR';
And the result end up like:
and
My question is, how can I combine two sql statements I wrote as 1 sql and also formatting the result displayed.
Would changing your where statement work?
Something like this:
SELECT OWNER, CONSTRAINT_NAME, TABLE_NAME, SEARCH_CONDITION, INDEX_NAME
FROM USER_CONSTRAINTS
WHERE TABLE_NAME = 'ACTOR' or table_name = 'film' or table_name ='film_actor';
I have table EMP with 2 columns ENM and EADD. Now I need to add a column EMPID but I want it to be displayed as the first column in the table, like EMPID, ENM, EADD.
Demo:
create table emp
( enm varchar2(20)
, eadd varchar2(100) );
alter table emp add empid number not null;
Describing emp gives:
Name Null? Type
------------------------------- -------- ----------------------------
ENM VARCHAR2(20)
EADD VARCHAR2(100)
EMPID NOT NULL NUMBER
Make all the columns except empid invisible and visible again:
begin
for r in (
select column_name
from user_tab_columns c
where c.table_name = 'EMP'
and c.column_name <> 'EMPID'
)
loop
execute immediate 'alter table emp modify '||r.column_name||' invisible';
execute immediate 'alter table emp modify '||r.column_name||' visible';
end loop;
end;
/
and now you have this:
Name Null? Type
------------------------------- -------- ----------------------------
EMPID NOT NULL NUMBER
ENM VARCHAR2(20)
EADD VARCHAR2(100)
As William Robertson suggests, using invisible and visible can be used to reorder columns. However if your table has a lot of fields this method can be obnoxious, especially if you are reordering multiple times during development.
There is always the tried and true method of dropping the table and recreating with the desired order.
I created table "A" with datatype VARCHAR2(3000) with "Not null" constraint. Table "A" has one column "someColumn" which is as primary key. As below:
CREATE TABLE A (
"someColumn" VARCHAR2(3000) NOT NULL
)
ALTER TABLE A
ADD CONSTRAINT pk_A PRIMARY KEY (
"someColumn"
)
Now I want change datatype from VARCHAR2(3000) to VARCHAR2(4000) but I don't want change constraint. So I used:
ALTER TABLE A
MODIFY
(
"someColumn" VARCHAR2(4000)
)
It worked and now I have ddl like below:
PROMPT CREATE TABLE a
CREATE TABLE a (
"someColumn" VARCHAR2(4000) NOT NULL
)
/
PROMPT ALTER TABLE a ADD CONSTRAINT pk_a PRIMARY KEY
ALTER TABLE a
ADD CONSTRAINT pk_a PRIMARY KEY (
"someColumn"
)
/
Then I used code as below:
ALTER TABLE A
MODIFY
(
"someColumn" VARCHAR2(3000) NULL
)
I got message "Alter table, executed..." but when I checked ddl again - I have "Not null" constraint still and new datatype (4000).
It's Oracle error?
To be sure that I don't have any cache in my "SQLTools" I am using:
SELECT * FROM all_tab_cols WHERE table_name = 'A'
Answer:
The change to NULLABLE fails but silently, so I see no error message.
on a column that is primary key you may change the length, but you can't do it nullable (because it is a PK which can't be NULL).
Initial state 3000 length NOT NULL
select COLUMN_NAME, DATA_LENGTH, NULLABLE from user_tab_columns where table_name = 'A';
COLUMN_NAME DATA_LENGTH N
------------------------------ ----------- -
someColumn 3000 N
Change length to 4000 - OK
ALTER TABLE A
MODIFY
(
"someColumn" VARCHAR2(4000)
)
;
select COLUMN_NAME, DATA_LENGTH, NULLABLE from user_tab_columns where table_name = 'A';
COLUMN_NAME DATA_LENGTH N
------------------------------ ----------- -
someColumn 4000 N
Change length to 3000 - OK
make it nullable - fails SILENTLY as PK can't be nullable
ALTER TABLE A
MODIFY
(
"someColumn" VARCHAR2(3000) NULL
);
select COLUMN_NAME, DATA_LENGTH, NULLABLE from user_tab_columns where table_name = 'A';
COLUMN_NAME DATA_LENGTH N
------------------------------ ----------- -
someColumn 3000 N
If you wan't to see a error message - split the change in two (change length and set nullable).
The first one will pass, the second will explicitely fail.
ALTER TABLE A
MODIFY
(
"someColumn" VARCHAR2(3000)
);
ALTER TABLE A
MODIFY
(
"someColumn" NULL
);
ORA-01451: column to be modified to NULL cannot be modified to NULL
I have a table that holds translations in an entire system and other tables reference to it, for example something like this:
Table "translations"
id | title
----------------------------
1 | First Translation
2 | Second Translation
And second table with foreign key pointing to translations:
Table "article"
id | translation_id | ...
1 | 1 | ...
I would like to get a list of rows that are not referenced by any other table (in this example row with id=2).
Number of tables might change and I would like to have a general solution that will operate on native relations mechanism in psql.
I've made the function you need. Bellow is the sample data I created to test it. In my data sample the return should be the ID 4 from the table t1. To your case the t1 table would be the translations table.
You have to change it to your tables. It shouldn't be difficult.
create table t1 (
id integer primary key not null,
lang varchar(10)
);
create table t2 (
id integer primary key not null,
id_t1 integer,
constraint fk_t2 foreign key (id_t1) references t1(id)
);
create table t3 (
id integer primary key not null,
id_t1 integer,
constraint fk_t3 foreign key (id_t1) references t1(id)
);
insert into t1 values (1, 'pt'), (2, 'us'), (3,'cn'), (4,'uk');
insert into t2 values (1, 1), (2,2);
insert into t3 values (1, 1), (2,3);
CREATE OR REPLACE FUNCTION listAllReferences()
RETURNS setof integer AS
$$
declare
fullSQL text;
rs RECORD;
begin
fullSQL := '';
for rs in
SELECT 'select t1.id from t1 inner join ' || tc.table_name || ' ON ('||tc.table_name||'.'||kcu.column_name||' = t1.id)' as sel
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
WHERE constraint_type = 'FOREIGN KEY'
AND ccu.table_name='t1' loop
if fullSQL != '' then
fullSQL := fullSQL || ' union ';
end if;
fullSQL := fullSQL || rs.sel;
end loop;
return query
execute 'select t1.id
from t1 left join ('||fullSQL||') alltb on (t1.id = alltb.id)
where alltb.id is null';
return;
end;
$$
LANGUAGE plpgsql;
And to use it just do:
select * from listAllReferences();
It will return:
listallreferences
4
Future tables with reference to your language table will also get covered because I'm getting the data from the INFORMATION_SCHEMA of PostgreSQL
Also you may have to add another filter () to the query on the implicit cursor which is AND tc.table_schema = 'yourSchemaName'