SQLite cascading delete - sql

The parent table is:
CREATE TABLE BHEAD (
ID INTEGER primary key asc,
DESCR TEXT,
LINECTR INT,
UNITCTR INT)
The child table is:
CREATE TABLE BDET (
ID INTEGER primary key asc,
BID INTEGER,
BCODE TEXT,
QTY INTEGER,
FOREIGN KEY (BID) REFERENCES BHEAD(ID) ON DELETE CASCADE
)
I also execute the SQL PRAGMA foreign_keys = ON;.
However, it does not work; when I delete one row from BHEAD, its associated rows in BDET are not gone...
Why was that?

What version of SQLite are you using?
Please see: Foreign Keys.
In order to use foreign key constraints in SQLite, the library must be compiled with neither SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER defined.

However, you can also implement on delete cascade to delete all child
rows when a parent row is deleted.
-- Create the test tables using ON DELETE CASCADE
DROP TABLE t3 PURGE;
--DROP TABLE t2 PURGE;
--DROP TABLE t1 PURGE;
CREATE TABLE t1 (
id NUMBER,
description VARCHAR2(50),
CONSTRAINT t1_pk PRIMARY KEY (id)
);
CREATE TABLE t2 (
id NUMBER,
t1_id NUMBER,
description VARCHAR2(50),
CONSTRAINT t2_pk PRIMARY KEY (id),
CONSTRAINT t2_t1_fk FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE
);
CREATE TABLE t3 (
id NUMBER,
t2_id NUMBER,
description VARCHAR2(50),
CONSTRAINT t3_pk PRIMARY KEY (id),
CONSTRAINT t3_t2_fk FOREIGN KEY (t2_id) REFERENCES t2 (id)
);
INSERT INTO t1 VALUES (1, 't1 ONE');
INSERT INTO t2 VALUES (1, 1, 't2 ONE');
INSERT INTO t2 VALUES (2, NULL, 't2 TWO');
INSERT INTO t3 VALUES (1, 1, 't3 ONE');
INSERT INTO t3 VALUES (2, NULL, 't3 TWO');
COMMIT;
SELECT (SELECT COUNT(*) FROM t1) AS t1_count,
(SELECT COUNT(*) FROM t2) AS t2_count,
(SELECT COUNT(*) FROM t3) AS t3_count
FROM dual;
DELETE FROM t3;
rollback;
truncate table t1 ;
rollback;
truncate table t1 CASCADE;

Related

Why `not deferrable` constraint is deferred when using `with`?

create table T1 (
id bigint NOT NULL primary key,
a bigint unique not null
);
create table T2 (
id int not null primary key,
b bigint,
foreign KEY(id) references T1(id) not deferrable
);
alter table T1 add constraint fk_id foreign key (id) references T2(id) not deferrable;
--Statement 1
with ins as (
insert into T1(id, a) values(15, 4) returning id
)
insert into T2(id, b) values(15, 3);
--Statement 2
with ins as (
insert into T2(id, b) values(14, 4) returning id
)
insert into T1(id, a) values(14, 3);
--Statement 3 (gives error)
with upd as (
update T1 set a = 4 where id = 14 returning id
)
update T1 set a = 3 where id = 15;
When this script is run in PostgreSQL, why does Statement 1 and 2 work and only statement 3 gives an error even when both the foreign key constraint are explicitly not deferrable?
According to the docs non-deferrable unique constraints are checked for each row, contrary to the standards specification that they are checked only at the end of a statement.
When a UNIQUE or PRIMARY KEY constraint is not deferrable, PostgreSQL checks for uniqueness immediately whenever a row is inserted or modified. The SQL standard says that uniqueness should be enforced only at the end of the statement...
But this exception to the standards spec is only for uniqueness, not for foreign key. Foreign key constraints are checked at the end of the statement if they are either not deferrable or if they are deferrable but not deferred. Since any problems have been cured by the end of the statement in your first two examples, there is no error.

Only one active for each relationship constraint

So i have this problem with my assignment.
I have two entities: Order, Gifr_cupon.
I have two tables: Orders, Gift_Cupons.
Each order can have many cupons or none. Each Cupon is bound to no or an single order. But only one cupon can be active for each order.
How to enforce this by constraint?
Heres a logical and ER view with DDL:
DLL:
CREATE TABLE gift_cupons (
cupon_id INTEGER NOT NULL,
order_order_id INTEGER,
active INTEGER NOT NULL
);
ALTER TABLE gift_cupons ADD CONSTRAINT gift_cupon_pk PRIMARY KEY ( cupon_id
);
ALTER TABLE gift_cupons ADD CHECK gift_cupon_check CHECK(active IS NULL OR ( active >= 0 AND active <=1 ) );
CREATE TABLE orders (
order_id INTEGER NOT NULL
);
ALTER TABLE orders ADD CONSTRAINT order_pk PRIMARY KEY ( order_id );
ALTER TABLE gift_cupons
ADD CONSTRAINT gift_cupon_order_fk FOREIGN KEY ( order_order_id )
REFERENCES orders ( order_id );
Kind of
Cupon - is bound to -> Order;
Order - has active -> Cupon;
Cupon (
Id PK,
orderId FK Order.Id,
Unique ( Id, orderId) -- any superset of PK is unique
);
Order (
Id PK
ActiveCuponId,
(Id, ActiveCuponId) FK Cupon( OrderId, Id)
);
See fiddle https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=596b30905d02a9e5c799b16da5fff5ab
Remove the ACTIVE column from the gift_cupons table, and replace this state by a foreign key in the orders table, as in:
CREATE TABLE gift_cupons (
cupon_id INTEGER NOT NULL,
order_order_id INTEGER,
);
ALTER TABLE gift_cupons ADD CONSTRAINT gift_cupon_pk PRIMARY KEY ( cupon_id
);
CREATE TABLE orders (
order_id INTEGER NOT NULL
active_cupon INTEGER -- nullable
);
ALTER TABLE orders ADD CONSTRAINT order_pk PRIMARY KEY ( order_id );
ALTER TABLE gift_cupons
ADD CONSTRAINT gift_cupon_order_fk FOREIGN KEY ( order_order_id )
REFERENCES orders ( order_id );
alter table orders
add constraint order_active_cupon_fk foreign key (active_cupon)
references gift_cupons (cupon_id);

'Disabled' constraints preventing MERGE OUTPUT from working

CREATE TABLE a(
id INT PRIMARY KEY IDENTITY(1,1),
val INT
);
CREATE TABLE b(
id INT PRIMARY KEY IDENTITY(1,1),
val INT
);
CREATE TABLE a_b(
fk_a INT,
fk_b INT
);
CREATE TABLE c(
id INT PRIMARY KEY IDENTITY(1,1),
a_val INT,
b_val INT
);
ALTER TABLE a_b ADD CONSTRAINT fk_a_b_fk_a FOREIGN KEY (fk_a) REFERENCES a(id);
ALTER TABLE a_b ADD CONSTRAINT fk_a_b_fk_b FOREIGN KEY (fk_b) REFERENCES b(id);
INSERT INTO a(val) VALUES (1), (2), (3);
INSERT INTO b(val) VALUES (2), (4), (6);
ALTER TABLE a_b NOCHECK CONSTRAINT ALL;
WITH cte AS (
SELECT
a.id AS 'a_id',
a.val AS 'a_val',
b.id AS 'b_id',
b.val AS 'b_val'
FROM a
INNER JOIN b
ON a.id = b.id
)
MERGE INTO c
USING cte ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (a_val, b_val)
VALUES (cte.a_val, cte.b_val)
OUTPUT cte.a_id, cte.b_id INTO a_b(fk_a, fk_b);
ALTER TABLE a_b WITH CHECK CHECK CONSTRAINT ALL;
SELECT * FROM c;
The error I'm getting is:
The target table 'a_b' of the OUTPUT INTO clause cannot be on either side of a (primary key, foreign key) relationship. Found reference constraint 'fk_a_b_fk_a'.
However, I'm very clearly disabling the constraints surrounding the merge statement. Is there any way to fix this?
The same thing goes for/is happening with triggers and disabling them surrounding the merge statement doesn't work.
try this
CREATE TABLE #a(
id INT PRIMARY KEY IDENTITY(1,1),
val INT
);
CREATE TABLE #b(
id INT PRIMARY KEY IDENTITY(1,1),
val INT
);
CREATE TABLE #a_b(
fk_a INT,
fk_b INT
);
CREATE TABLE #c(
id INT PRIMARY KEY IDENTITY(1,1),
a_val INT,
b_val INT
);
ALTER TABLE #a_b ADD CONSTRAINT fk_a_b_fk_a FOREIGN KEY (fk_a) REFERENCES a(id);
ALTER TABLE #a_b ADD CONSTRAINT fk_a_b_fk_b FOREIGN KEY (fk_b) REFERENCES b(id);
INSERT INTO #a(val) VALUES (1), (2), (3);
INSERT INTO #b(val) VALUES (2), (4), (6);
WITH cte AS (
SELECT
a.id AS 'a_id',
a.val AS 'a_val',
b.id AS 'b_id',
b.val AS 'b_val'
FROM #a a
INNER JOIN #b b
ON a.id = b.id
)
MERGE INTO #c
USING cte ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (a_val, b_val)
VALUES (cte.a_val, cte.b_val)
OUTPUT cte.a_id, cte.b_id INTO #a_b(fk_a, fk_b);
SELECT * FROM #c;

Not expected result of the query

There is a query:
select * from
(
select
p.ID
from
MY_PRODUCT_PARENT_LINK pLink
inner join MY_PRODUCT p on pLink.FK_PARENT=p.ID
order by
pLink.DESCENDANT_LVL desc
)
where rownum <= 1
;
The inner query returns one row with not null value of p.ID.
The outer query - one row with null value of p.ID. Here expected not null value.
The database sever is Oracle 11g.
There is a code to reproduction:
CREATE TABLE MY_PRODUCT
(
FK_PARENT NUMBER(19),
ID NUMBER(19) NOT NULL,
NAME VARCHAR2(4000 BYTE),
CONSTRAINT PK_MY_PRODUCT PRIMARY KEY (ID)
);
INSERT INTO MY_PRODUCT (FK_PARENT, ID,NAME) VALUES (null, 111111,'PRODUCT_NAME_01');
INSERT INTO MY_PRODUCT (FK_PARENT, ID,NAME) VALUES (null, 111112,'PRODUCT_NAME_02');
CREATE TABLE MY_PRODUCT_PARENT_LINK
(
ID NUMBER(19) NOT NULL,
FK_PRODUCT NUMBER(19) NOT NULL,
FK_PARENT NUMBER(19) NOT NULL,
DESCENDANT_LVL NUMBER(19) NOT NULL,
primary key (ID)
);
ALTER TABLE MY_PRODUCT_PARENT_LINK ADD
CONSTRAINT MY_PRD_PARENT_TO_MY_PRODUCT
FOREIGN KEY (FK_PRODUCT)
REFERENCES MY_PRODUCT (ID) ON DELETE CASCADE;
ALTER TABLE MY_PRODUCT_PARENT_LINK ADD
CONSTRAINT MY_PRD_PARENT_TO_PARENT
FOREIGN KEY (FK_PARENT)
REFERENCES MY_PRODUCT (ID) ON DELETE CASCADE;
INSERT INTO MY_PRODUCT_PARENT_LINK (ID, FK_PRODUCT, FK_PARENT, DESCENDANT_LVL) VALUES (211111, 111112, 111111, 1);
Try and select the ID in the outer query and see if that returns the result. so instead of select * in the outer do select ID.

How to insert row in table with foreign key to itself?

I have table that has foreign key for itself. Column parentid is foreign key and it cannot be NULL.
if I doINSERT INTO mytable(name) VALUES('name'), so it says that can't insert NULL to parentid. BUT, what value I can set to it if no row was inserted yet?!
How I can write script that will add row to this table?
Thank you
Remove the NOT NULL constraint, as it is an inappropriate constraint. If you do not have a ParentId then the value is NULL and should be allowed. Creating a dummy row just to have a dummy parentid creates unnecessary dependencies.
A trick: Have a dummy row with a dummy key, say 99999. Insert with this as the FK, and then change the FK to its real value. And do it in a transaction.
Disable the FK in charge.
Then do the insert
Then do an update with the new (generated?) PK-ID into the Self-FK-Field
Then Enable the FK back.
LIke so:
ALTER TABLE [Client] NOCHECK CONSTRAINT [FK_Client_MainClient]
INSERT INTO Client VALUES ...
#ClientID = SCOPE_IDENTITY()
IF #IsMainClient=1
BEGIN
UPDATE [Client]
SET MainClientID = #ClientID
WHERE ClientID = #ClientID
END
ALTER TABLE [Relatie] WITH CHECK CHECK CONSTRAINT [FK_Relatie_Relatie]
How to make a dummy row with both id and parentid equal to -1:
CREATE TABLE mytable(
id int NOT NULL IDENTITY,
parentid int NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (parentid) REFERENCES mytable(id)
) ;
SET IDENTITY_INSERT mytable ON ; <-- this allows you to insert the
INSERT INTO mytable(id, parentid) <-- auto incremented identity field
VALUES (-1, -1);
SET IDENTITY_INSERT mytable OFF ;
SELECT * FROM mytable ;
| id | parentid |
| -1 | -1 |
If you have many data from other tables that you want to transfer into this table, you can set the IDENTITY_INSERT variable to ON, insert the data and then set it to OFF again.
But as others said, it might be better to just remove the NOT NULL constraint from the parentid field.
You can alter the column to allow null then set the fk to the new identity and enable not null again.
This should work, though maybe there is a better way
CREATE TABLE mytable
(
id int IDENTITY(1,1) primary key,
name varchar(50) not null,
parentid int not null
)
go
alter table mytable
add constraint FK_mytable_parentid FOREIGN KEY ( parentid ) references mytable(id)
ALTER TABLE mytable alter column parentid int null;
insert into mytable(name)
select 'test'
update mytable
set parentid = SCOPE_IDENTITY()
where id = SCOPE_IDENTITY()
ALTER TABLE mytable alter column parentid int not null;
select * from mytable
drop table mytable
From what I understood you already have id before inserting and you can't insert it because identity field isn't letting you to.
Like you mentioned in your comment:
in 1 table I have the rows 34 'name1'
34, 35 'name2' 34 (id,name,parentid)
and I want to copy them to other table
First table
create table Table1
(
id int not null primary key,
name varchar(20) not null,
parentId int not null
)
insert Table1
values
(34, 'name1', 34),
(35, 'name2', 34)
Second table:
create table Table2
(
id int identity(1, 1) primary key,
name varchar(20) not null,
parentId int not null foreign key references Table2(id)
)
Copying data from the first table to the second one:
set identity_insert Table2 on
insert Table2(id, name, parentId)
select *
from Table1
set identity_insert Table2 on
[Update]
If the second table already has values then:
alter table Table2
add oldId int null
alter table Table2
alter column parentId int null
go
insert Table2(name, oldId)
select name, id
from Table1
update tt3
set parentId = tt2.id
from Table2 tt3
join Table1 tt1 on
tt1.id = tt3.oldId
join Table2 tt2 on
tt1.parentId = tt2.oldId
alter table Table2
drop column oldId
alter table Table2
alter column parentId int not null
Don't reference an IDENTITY column as a self-referencing foreign key. Use an alternative key of the table instead. I guess you are using IDENTITY as a surrogate key but every table ought to have a natural key as well, so the IDENTITY column shouldn't be the only key of your table.