Issue while updating Primary and foreign key using deferred FK - sql

I am trying to update my primary and foreign key. After searching a lot, I came across the concept of a deferred key. I am able to update my PK and FK using SQLscript in PL/SQL. However I am getting an error while doing the same thing using PHP. Below is my code for PHP.
$update_p="update project set project_id='$projectname' where project_id='$previousproject'";
$up=oci_parse($conn,$update_p);
$update_m="update members set project='$projectname' where project='$previousproject'";
$um=oci_parse($conn,$update_m);
$commit="commit";
$c=oci_parse($conn,$commit);
oci_execute($up);
oci_execute($um);
oci_execute($c);
echo"Project Updated!";

I don't know how you were able to do that in PL/SQL script, but it's not enough to declare a constraint as deferrable.
You should also mark a deferrable constraint as deferred in your transaction:
Specify DEFERRED to indicate that the conditions specified by the
deferrable constraint are checked when the transaction is committed.
So you need to make your constraints as deferred before updating the table.

This is because you need to explicitly set the session as well as the key to allow for deferred constraints.
If we assume the following environment:
create table tmp_1 (
pk number
, fk number
, constraint pk_tmp_1 primary key (pk)
);
create table tmp_2 (
pk number
, fk number
, constraint pk_tmp_2 primary key (pk)
, constraint fk_tmp_2 foreign key (fk) references tmp_1 (pk)
);
alter table tmp_1
add constraint fk_tmp_1
foreign key (fk)
references tmp_2(pk)
deferrable;
If you attempt an insert you'll get the following error:
SQL> insert into tmp_1 values (1, 2);
insert into tmp_1 values (1, 2)
*
ERROR at line 1:
ORA-02291: integrity constraint (MF.FK_TMP_1) violated - parent key not found
SQL> insert into tmp_2 values (2, 1);
insert into tmp_2 values (2, 1)
*
ERROR at line 1:
ORA-02291: integrity constraint (MF.FK_TMP_2) violated - parent key not found
However, by altering the session to set the constraints parameter to deferred this will work:
SQL> alter session set constraints = deferred;
Session altered.
SQL>
SQL> insert into tmp_1 values (1, 2);
1 row created.
SQL> insert into tmp_2 values (2, 1);
1 row created.
Here, deferred "indicates that the conditions specified by the deferrable constraint are checked when the transaction is committed" as opposed to when the DML occurs.
Though circular dependencies can sometimes be necessary Oracle doesn't go out of it's way to make life easy for you - it shouldn't. If you have circular dependencies it's always worth re-evaluating your data-model to ensure that these are required.

Related

Create constraint for control insert in table

There are two tables - orders and a list of services. In the first there is a bool field that the order is approved, if it is true then you can’t insert / delete values in the second table. With the UPDATE of the first table and the DELETE of the second, it is clear.
INSERT make as
INSERT INTO b (a_id, b_value)
SELECT *
FROM (VALUES (1, 'AA1-BB1'),(1, 'AA1-BB2'),(1, 'AA1-BB3')) va
WHERE (SELECT NOT confirm FROM a WHERE a_id = 2);
https://dbfiddle.uk/?rdbms=postgres_12&fiddle=7b0086967c1c38b0c80ca5624ebe92e9
How to forbid to insert without triggers and stored procedures? Is it possible to compose somehow complex constraint or a foreign key for checking conditions at the DBMS level?
The most recent version of Postgres supports generated columns. So, you can do:
alter table b add confirm boolean generated always as (false) stored;
Then create a unique key in a:
alter table a add constraint unq_a_confirm_id unique (confirm, id);
And finally the foreign key relationship:
alter table b add constraint fk_b_a_id_confirm
foreign key (confirm, a_id) references a(confirm, id);
Now, only confirmed = false ids can be used. Note that this will prevent updates to a that would invalidate the foreign key constraint.

"references" statement in SQL

I have a table in SQL like;
table Court
CourtID numeric(4) primary key
Name varchar(40) not null
Place varchar(40) not null
Type varchar(3) not null
TypeID numeric(4) references Court(CourtID) default null
I could not find info about what that reference statement stands for and how does it relate TypeID with CourtID?
It is simply shorthand syntax for a FOREIGN KEY.
All sorts of Google results are found with "sql references keyword"
Or simply trying it can often help more than a Google search (the old fashioned way).
Your example shows a self-referential foreign key. It is a common pattern seen to model relationships like PARENT or SPOUSE, where all records belong in the same base table, but may reference each other.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL> create table court(
2 courtid integer primary key,
3 typeid integer references court(courtid)
4 );
Table created.
SQL> insert into court values(1,0);
insert into court values(1,0)
*
ERROR at line 1:
ORA-02291: integrity constraint (MSMITH.SYS_C0016710) violated - parent key not
found
SQL> insert into court values(1,1);
1 row created.
The above syntax will generate a "random" name for the constraint. A better syntax is to explicitly name the constraint. Notice what happens if I recreate it with additional FOREIGN KEY syntax.
SQL> create table court(
2 courtid integer primary key,
3 typeid integer,
4 constraint fk_typeid foreign key (typeid) references court(courtid)
5 );
Table created.
SQL> insert into court values(1,0);
insert into court values(1,0)
*
ERROR at line 1:
ORA-02291: integrity constraint (MSMITH.FK_TYPEID) violated - parent key not
found
The key is now named FK_TYPEID rather than SYS_C0016710

What are cons and pros a of defining a constraint deferrable

We are using Oracle database in our projects. And we define as much as constraints that can be applied to database, (including primary, unique, check and foreign key constraints).
It seems that defining constraints DEFERRABLE allows us to DEFER them when it is required, so why shall any constraint be defined as NOT DEFERRABLE?
Why databases such as Oracle have NOT DEFERRABLE as default case?
Are there any pros for defining a constraint NOT DEFERRABLE?
The major use case for deferrable constraints is that you don't need to worry about the order in which you do DML statements for multiple tables that have a foreign key relationship.
Consider the following example:
create table parent
(
id integer not null primary key
);
create table child
(
id integer not null primary key,
parent_id integer not null references parent
);
create table grand_child
(
id integer not null primary key,
child_id integer not null references child
);
If the constraints are immediate you have to insert (or delete) rows (that reference each other) in the proper sequence, which can e.g. be a problem when bulk loading data. If the constraints are deferred you can insert/delete the rows in any sequence as long as everything is fine when you commit your transaction.
So with a deferrable constraint (which the above example does not create!) you could the following:
insert into grand_child values (1,1);
insert into child values (1,1);
insert into parent values (1);
commit;
That would not be possible if the constraints were immediate.
A special case of the above example are cyclic references:
create table one
(
id integer not null primary key,
id_two integer not null
);
create table two
(
id integer not null primary key
id_one integer not null
);
alter table one add constraint fk_one_two (id_two) references two(id);
alter table two add constraint fk_two_one (id_one) references one(id);
Without declaring the constraints as deferrable you will not be able to insert data into those tables at all.
The workaround for DBMS that do not support deferrable constraints would be to make the fk columns nullable. And then insert null values first:
insert into one values (1, null);
insert into two values (1, 1);
update one
set id_two = 1
where id = 1;
With a deferrable constraint you don't need the additional update statement.
(The design using a cyclic reference is however very often questionable!)
I don't use deferrable constraints where often, but I wouldn't want to live without them.
One drawback of deferrable constraints is error checking though. You don't know until you commit if your data is correct. That makes finding out what went wrong a bit more complicated. If you get the error when doing the insert (or delete or update) you immediately know which values caused the error.

How do I switch two ID [PK] on postgres database?

I want to change the ID on two rows on Postgres, to switch them. They are already defined as foreign key so I cannot use a third number to do the switch.
How can I do this in one SQL query or transaction?
Example:
UPDATE mytable SET id=2 WHERE ID=1;
UPDATE mytable SET id=1 WHERE ID=2
You mention foreign keys, but it remains unclear whether id is the referenced or the referencing column of a foreign key constraint.
If id is the referenced column you just define the fk constraint ON UPDATE CASCADE. Then you can change your id as much as you want. Changes are cascaded to the depending columns.
If id is the referencing column (and no other foreign key constraints point to it), then there is another, faster way since PostgreSQL 9.0. You can use a deferrable primary or unique key. Consider the following demo:
Note that you can't use this if you want to reference id with a foreign key constraint from another table. I quote the manual here:
The referenced columns must be the columns of a non-deferrable unique
or primary key constraint in the referenced table.
Testbed:
CREATE TEMP TABLE t
( id integer
,txt text
,CONSTRAINT t_pkey PRIMARY KEY (id) DEFERRABLE INITIALLY DEFERRED
);
INSERT INTO t VALUES
(1, 'one')
,(2, 'two');
Update:
UPDATE t
SET id = t_old.id
FROM t t_old
WHERE (t.id, t_old.id) IN ((1,2), (2,1));
Result:
SELECT * FROM t;
id | txt
---+-----
2 | one
1 | two
You can also declare the constraint DEFERRABLE INITIALLY IMMEDIATE and use SET CONSTRAINTS ... DEFERRED in the same transaction.
Be sure to read about the details in the manual:
CREATE TABLE
SET CONSTRAINTS
Even seems to work with DEFERRABLE INITIALLY IMMEDIATE and no SET CONSTRAINTS. I posted a question about that.
begin;
alter table mytable drop constraint_name;
UPDATE mytable SET id=-1 WHERE ID=1;
UPDATE mytable SET id=1 WHERE ID=2;
UPDATE mytable SET id=2 WHERE ID=-1;
alter table mytable add table_constraint;
commit;
Have you tried something like:
BEGIN;
CREATE TEMP TABLE updates ON COMMIT DROP AS
SELECT column1::int oldid, column2::int newid
FROM ( VALUES (1, 2), (2, 1) ) foo;
UPDATE mytable
FROM updates
SET id = newid
WHERE id = oldid;
--COMMIT;
ROLLBACK;
Of course rollback gets commented out and commit in when you are ready to go.

PostgreSQL delete fails with ON DELETE rule on inherited table

In my PostgreSQL 9.1 database I've defined RULEs that delete rows from child tables whenever a parent table row is deleted. This all worked OK, until I introduced inheritance. If the parent (referencing) table INHERITS from another table and I delete from the base table then the DELETE succeeds, but the RULE doesn't appear to fire at all - the referenced row is not deleted. If I try to delete from the derived table I get an error:
update or delete on table "referenced" violates foreign key constraint "fk_derived_referenced" on table "derived"
There is no other row in the parent table that would violate the foreign key: it's being referenced by the row that's being deleted! How do I fix this?
The following script reproduces the problem:
-- Schema
CREATE TABLE base
(
id serial NOT NULL,
name character varying(100),
CONSTRAINT pk_base PRIMARY KEY (id)
);
CREATE TABLE referenced
(
id serial NOT NULL,
value character varying(100),
CONSTRAINT pk_referenced PRIMARY KEY (id)
);
CREATE TABLE derived
(
referenced_id integer,
CONSTRAINT pk_derived PRIMARY KEY (id),
CONSTRAINT fk_derived_referenced FOREIGN KEY (referenced_id) REFERENCES referenced (id)
)
INHERITS (base);
-- The rule
CREATE OR REPLACE RULE rl_derived_delete_referenced
AS ON DELETE TO derived DO ALSO
DELETE FROM referenced r WHERE r.id = old.referenced_id;
-- Some test data
INSERT INTO referenced (id, value)
VALUES (1, 'referenced 1');
INSERT INTO derived (id, name, referenced_id)
VALUES (2, 'derived 2', 1);
-- Delete from base - deletes the "base" and "derived" rows, but not "referenced"
--DELETE FROM base
--WHERE id = 2;
-- Delete from derived - fails with:
-- update or delete on table "referenced" violates foreign key constraint "fk_derived_referenced" on table "derived"
DELETE FROM derived
WHERE id = 2
As I said in my comment, this seems an unusual way to do things. But you can make it work with a deferred constraint.
CREATE TABLE derived
(
referenced_id integer,
CONSTRAINT pk_derived PRIMARY KEY (id),
CONSTRAINT fk_derived_referenced FOREIGN KEY (referenced_id)
REFERENCES referenced (id) DEFERRABLE INITIALLY DEFERRED
)
INHERITS (base);
The PostgreSQL docs, Rules vs. Triggers, say
Many things that can be done using triggers can also be implemented
using the PostgreSQL rule system. One of the things that cannot be
implemented by rules are some kinds of constraints, especially foreign
keys.
But it's not clear to me that this specific limitation is what you're running into.
Also, you need to check if other records are still referencing the to-be-deleted rows. I added a test derived record#3, which points to the same #1 reference record.
-- The rule
CREATE OR REPLACE RULE rl_derived_delete_referenced
AS ON DELETE TO tmp.derived DO ALSO (
DELETE FROM tmp.referenced re_del
WHERE re_del.id = OLD.referenced_id
AND NOT EXISTS ( SELECT * FROM tmp.derived other
WHERE other.referenced_id = re_del.id
AND other.id <> OLD.id )
;
);
-- Some test data
INSERT INTO tmp.referenced (id, value)
VALUES (1, 'referenced 1');
-- EXPLAIN ANALYZE
INSERT INTO tmp.derived (id, name, referenced_id)
VALUES (2, 'derived 2', 1);
INSERT INTO tmp.derived (id, name, referenced_id)
VALUES (3, 'derived 3', 1);
-- Delete from base - deletes the "base" and "derived" rows, but not "referenced"
--DELETE FROM base
--WHERE id = 2;
-- Delete from derived - fails with:
-- update or delete on table "referenced" violates foreign key constraint "fk_derived_referenced" on table "derived"
EXPLAIN ANALYZE
DELETE FROM tmp.derived
WHERE id = 2
;
SELECT * FROM tmp.base;
SELECT * FROM tmp.derived;
SELECT * FROM tmp.referenced;