What is the best way to empty a self-referential MySQL table? - sql

I have a self-referential MySQL table with a recursive parent_id:
CREATE TABLE `recursive` (
`id` int(11) NOT NULL auto_increment,
`parent_id` int(11) default NULL,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
KEY `data_categorysource_parent_id` (`parent_id`),
CONSTRAINT `parent_id_refs_id_627b4293`
FOREIGN KEY (`parent_id`) REFERENCES `data_categorysource` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
During testing, I want to empty it but TRUNCATE fails:
TRUNCATE `recursive`
/* SQL Error: Cannot delete or update a parent row: a foreign key
constraint fails...
I currently have to manually delete all records, starting at the bottom of the tree working upwards. This gets onerous even with small trees.
Is there an easy way around this? I can't DROP the table and re-create it easily as other tables reference it (I have already truncated those so there should be no data integrity issues there).

Why not:
UPDATE 'recursive' SET 'parent_id' = NULL WHERE 'parent_id' IS NOT NULL;
DELETE FROM 'recursive';
?

If you just want to empty the whole thing for testing purposes use:
SET FOREIGN_KEY_CHECKS = 0;
// Execute Query
SET FOREIGN_KEY_CHECKS = 1;
This totally bypasses any foreign key checks.

Well, you could add an ON DELETE CASCADE to the FOREIGN KEY definition... at least temporarily. That would allow you to truncate the table by removing the referenced rows first.
There are other ON DELETE types as well; the default is ON DELETE NO ACTION.

Or just remove the (recursive) foreign key constraint, then truncate the table, then re-add the contraint.

Repeatedly select the rows that do not appear as parents and delete them, until the table is empty. (Assuming there are no cycles...)

delete from table_1 where date(table_1_TIME) < (select T.t_Date from (select max(date(table_1_TIME)) as t_Date from table_1 ) as T)

Related

Don't know why I'm getting a foreign constraint error in sqlite

So I've created these two tables in SQLite:
CREATE TABLE CONFLUENCE_LINKS (
longLink TEXT NOT NULL
PRIMARY KEY,
shortLink TEXT NOT NULL
DEFAULT [short link not found yet],
dateLastUpdated TEXT NOT NULL
DEFAULT [last updated date will be added soon]
);
CREATE TABLE LOG_CONFLUENCE_PAGES_TO_FIX (
shortLink TEXT NOT NULL
PRIMARY KEY,
dateChecked TEXT NOT NULL,
numDaysMissingAltText INTEGER NOT NULL,
FOREIGN KEY (
shortLink
)
REFERENCES CONFLUENCE_LINKS (shortLink) ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE CONFLUENCE_PAGES_MISSING_ALT_TEXT (
shortLink TEXT NOT NULL
PRIMARY KEY,
pageName TEXT NOT NULL,
hasImages TEXT NOT NULL
DEFAULT FALSE,
hasAltText TEXT NOT NULL
DEFAULT FALSE,
imageNamesLinks TEXT NOT NULL,
FOREIGN KEY (
shortLink
)
REFERENCES CONFLUENCE_LINKS (shortLink) ON DELETE CASCADE
ON UPDATE CASCADE
Then I added a row to CONFLUENCE_LINKS:
INSERT INTO CONFLUENCE_LINKS (longLink) VALUES ("espn.com")
Then I try to remove this row and commit this change using the GUI buttons in SQLiteStudio. SQL won't remove this row; I get this error instead:
Error while deleting row from table CONFLUENCE_LINKS: foreign key mismatch - "CONFLUENCE_PAGES_MISSING_ALT_TEXT" referencing "CONFLUENCE_LINKS"
I'd kinda understand getting this error if I was deleting data from CONFLUENCE_LINKS (parent table) that was actually getting referenced in CONFLUENCE_PAGES_MISSING_ALT_TEXT (child table). But CONFLUENCE_PAGES_MISSING_ALT_TEXT contains no data! So I don't understand why I'm getting this error now.
I'm using Linux Mint 20 btw, if that helps anything.
EDIT
My bad for forgetting to include one of the key table definitions. I've added it above.
So I'm unable to replicate the error too, if I run the DELETE statement from the Linux terminal:
$sqlite3 NAME_OF_DB
sqlite> DELETE FROM CONFLUENCE_LINKS WHERE longLink = 'espn.com';
But I do get the error if I delete this row using the GUI buttons in SQLiteStudio (v3.3.3). At first I thought the issue was with this db manager, so I downloaded and tested a bunch of other db managers. The error persisted with these other db managers too, which suggests the issue is likely with my code.
Turns out I needed to add an INDEX table too.
Here's that third INDEX table:
CREATE UNIQUE INDEX UX_CONFLUENCE_LINKS_SHORTLINK ON CONFLUENCE_LINKS(shortLink)
Everything is working now!!!
Below are some references that helped me:
Answer from a StackExchange post
SQLite documentation on db indexes
W3Schools page on SQL UNIQUE constraint

Updating primary keys in POSTGRESQL

I have a database from previous project that I want to use in another project, from security reasons I need to update the IDs of one of the table. Problem is that the table is heavily referenced by foreign keys from other tables:
CREATE TABLE "table_table" (
"id" serial NOT NULL PRIMARY KEY,
"created" timestamp with time zone NOT NULL,
);
CREATE TABLE "table_photo" (
"id" serial NOT NULL PRIMARY KEY,
"table_id" integer NOT NULL REFERENCES "table_table" ("id") DEFERRABLE INITIALLY DEFERRED,
);
Now if I change the id on table_table the reference from table_photo won't work.
I will probably use something like this to change the IDs:
UPDATE table_table SET id = id + 15613;
I have read somewhere that I could use ON UPDATE CASCADE constraints to do this but I am not very sure how to use it.
btw: I am using Django ORM.
Get the constraint name with \d "table_photo", which shows:
Foreign-key constraints:
"table_photo_table_id_fkey" FOREIGN KEY (table_id) REFERENCES table_table(id) DEFERRABLE INITIALLY DEFERRED
Then replace it with a constraint that has on update cascade:
ALTER TABLE "table_photo"
DROP CONSTRAINT "table_photo_table_id_fkey",
ADD CONSTRAINT "table_photo_table_id_fkey"
FOREIGN KEY ("table_id")
REFERENCES "table_table"
ON UPDATE CASCADE
DEFERRABLE INITIALLY DEFERRED;
Now when you do your UPDATE, referenced row IDs are automatically updated. Adding an index on "table_photo"."table_id" will help a lot.
This can be slow for big tables though. An alternative if you have large tables is to do it in a couple of stages. For table A with field id that's referenced by table B's field A_id:
Add a new column, new_id, to A, with a UNIQUE constraint. Leave it nullable.
Add a new column, A_new_id to table B, giving it a foreign key constraint to A(new_id).
Populate A.new_id with the new values
Do an
UPDATE B
SET A_new_id = A.new_id
FROM A
WHERE B.A_id = A.id;
to do a joined update, setting the new ID values in B.A_new_id to match.
Drop the column B.A_id and rename B.A_new_id to B.A_id.
Drop the column A.id and rename A.new_id to A.id
Create a PRIMARY KEY constraint on the renamed A.id, USING the index created automatically before.
It's a lot more complicated, especially since for big tables you usually want to do each of these steps in batches.
If this seems too complicated, just do it with a cascading foreign key constraint like above.

ERROR 1451: Cannot delete or update a parent row: a foreign key constraint fails

CREATE TABLE `categories` (
`idcategories` INT NOT NULL AUTO_INCREMENT ,
`idparent` INT NULL ,
`description` VARCHAR(45) NULL ,
PRIMARY KEY (`idcategories`) );
ALTER TABLE `categories`
ADD CONSTRAINT `FK_idparent`
FOREIGN KEY (`idparent` )
REFERENCES `ilmercatinodelpulcino`.`categories` (`idcategories` )
ON DELETE CASCADE
ON UPDATE CASCADE
, ADD INDEX `FK_idparent` (`idparent` ASC) ;
INSERT INTO `categories` (`idcategories`, `description`)
VALUES (1, 'cat1');
INSERT INTO `categories` (`idcategories`, `idparent`, `description`)
VALUES (2, 1, 'cat1_child');
So this table represents a category, with an ID and a self pointing parent ID.
I have inserted a category cat1 and a subcategory cat1_child with parent id of cat1.
Now, I want to be able to change idcategory of cat1 from 1 to 10 and because I set the foreign key on update CASCADE, I expect that idparent of cat1_child will be set to 10 as well.
But when I do:
UPDATE `categories` SET `idcategories`=10 WHERE `idcategories`='1';
I get an error:
ERROR 1451: Cannot delete or update a parent row: a foreign key
constraint fails (categories, CONSTRAINT FK_idparent FOREIGN KEY
(idparent) REFERENCES categories (idcategories) ON DELETE
CASCADE ON UPDATE CASCADE) SQL Statement: UPDATE categories SET
idcategories=10 WHERE idcategories='1'
The delete instead work as expected and deleting cat1, cat1_child will be deleted as well.
Where is the error?
Than you.
I believe the answer is in the documentation (scroll down to the bottom):
Deviation from SQL standards: If ON UPDATE CASCADE or ON UPDATE SET
NULL recurses to update the same table it has previously updated
during the cascade, it acts like RESTRICT. This means that you cannot
use self-referential ON UPDATE CASCADE or ON UPDATE SET NULL
operations. This is to prevent infinite loops resulting from cascaded
updates. A self-referential ON DELETE SET NULL, on the other hand, is
possible, as is a self-referential ON DELETE CASCADE. Cascading
operations may not be nested more than 15 levels deep.
Demo: http://www.sqlfiddle.com/#!2/e29db/1
I faced the same issue then i disabled the foreign key check using below query and then i was able to delete the row
SET FOREIGN_KEY_CHECKS=0;
You can also enable the foreign key check using below query
SET FOREIGN_KEY_CHECKS=1;

postgresql and Delete statement violates foreign key constraint

I have a problem with my delete statement.
I have two tables:
table vehicule_loan(
vehicule TEXT NOT NULL UNIQUE,
);
table vehicule_uid (
id UUID NOT NULL DEFAULT uuid_generate_v4(),
vehicule TEXT NOT NULL REFERENCES vehicule_loan(vehicule) ON DELETE NO ACTION
);
When I delete a vehicule from the table vehicule_loan I want that referencing rows in the table vehicule_uid are kept.
But when I try to delete one I get this error:
ERROR: update or delete on table "vehicule_loan" violates foreign key constraint "vehicule_uid_vehicule_fkey" on table "vehicule_uid"
I think I understand the error:
After I delete a vehicule from the table vehicule_loan, the vehicule in vehicule_uid would point to nothing.
But is there a way to keep the rows in vehicule_uid ?
You should allow NULL values in the foreign key attribute and define the foreign key constraint as ON DELETE SET NULL.
I quote chapter 5.3. Constraints from the PostgreSQL manual:
There are two other options: SET NULL and SET DEFAULT. These cause the
referencing columns to be set to nulls or default values,
respectively, when the referenced row is deleted.
Could look like this:
table vehicule_uid (
id uuid NOT NULL DEFAULT uuid_generate_v4(),
vehicule text REFERENCES vehicule_loan(vehicule) ON DELETE SET NULL
);
With this setting, when you delete a row in vehicule_loan all referencing rows in vehicule_uid remain in database.

Stop invalid data in a attribute with foreign key constraint using triggers?

How to specify a trigger which checks if the data inserted into a tables foreign key attribute, actually exists in the references table. If it exist no action should be performed , else the trigger should delete the inserted tuple.
Eg: Consider have 2 tables
R(A int Primary Key) and
S(B int Primary Key , A int Foreign Key References R(A) ) .
I have written a trigger like this :
Create Trigger DelS
BEFORE INSERT ON S
FOR EACH ROW
BEGIN
Delete FROM S where New.A <> ( Select * from R;) );
End;
I am sure I am making a mistake while specifying the inner sub query within the Begin and end Blocks of the trigger. My question is how do I make such a trigger ?
Wouldn't a foreign key constraint better achieve what you want?
ALTER TABLE [dbo].[TABLE2] WITH CHECK
ADD CONSTRAINT [FK_TABLE2_TABLE1] FOREIGN KEY([FK_COLUMN])
REFERENCES [dbo].[TABLE1] ([PK_COLUMN])
GO
This is what foreign key constraints are meant to do - specifically, not allow a record to be inserted that violate the foreign key relationship.
Note that to make this example more readable, I used different column and table names - S, A, R and B looked like a mess.