MySQL innoDB foreign key delete cascade from three tables - php

I have a MySQL database with several tables, connected with linking tables. My problem was that DELETE statements were very complicated. So in an attempt to make them simpler I tried setting up foreign keys with cascade deletes.
Here is my table structure
--------------
-n_size_class-
--------------
-s_id -
-s_name -
--------------
-------------
-n_size_rows-
-------------
-sr_id -
-sr_name -
-sr_value -
-------------
----------------
-n_size_columns-
----------------
-sc_id -
-sc_name -
-sc_value -
----------------
-----------------
-n_size_row_link-
-----------------
-srl_id -
-srl_size -
-srl_row -
-----------------
-----------------
-n_size_col_link-
-----------------
-scl_id -
-scl_size -
-scl_col -
-----------------
The idea is that the n_size_class table is the primary object (a size class) and then the rows and columns are children of the size class. The linking tables are then used to tie rows and columns to the size class. Previously, my inserts worked fine and deletes were a problem. Now that I tried setting up delete cascades, my inserts are broken (but my deletes work fine). My goal is to make inserts work properly, where size_class is inserted, then for each row, it is inserted into size_row and then also inserted into size_row_link, and the same for columns. Then when you delete just the size_class, all links and rows and columns are deleted as well.
What is the proper way to foreign key/cascade these tables? Right now, I get a foreign key constraint error when I try to insert anything into the columns or row table.
As per request, the create statements:
CREATE TABLE `n_size_class` (
`s_id` int(11) NOT NULL auto_increment,
`s_name` text NOT NULL,
PRIMARY KEY (`s_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE `n_size_columns` (
`sc_id` int(11) NOT NULL auto_increment,
`sc_name` text NOT NULL,
`sc_value` text NOT NULL,
PRIMARY KEY (`sc_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE `n_size_rows` (
`sr_id` int(11) NOT NULL auto_increment,
`sr_name` text NOT NULL,
`sr_value` text NOT NULL,
PRIMARY KEY (`sr_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE `n_size_row_link` (
`srl_id` int(11) NOT NULL auto_increment,
`srl_size` int(11) NOT NULL,
`srl_row` int(11) NOT NULL,
PRIMARY KEY (`srl_id`),
KEY `srl_row` (`srl_row`),
KEY `srl_size` (`srl_size`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE `n_size_col_link` (
`scl_id` int(11) NOT NULL auto_increment,
`scl_size` int(11) NOT NULL,
`scl_col` int(11) NOT NULL,
PRIMARY KEY (`scl_id`),
KEY `scl_col` (`scl_col`),
KEY `scl_size` (`scl_size`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;

The way I see it you have 3 objects in play: Sizes, Rows, and Columns.
I'm assuming:
That the linking tables are because a Size can be assigned to many Rows/Columns, and Rows/Columns can have many Sizes.
That when a Size is deleted the assignments should be deleted, no the Rows/Columns themselves.
That when a Row/Column is deleted the assignments should be deleted, not the Sizes themselves.
So:
CREATE TABLE sizes (
size_id INTEGER UNSIGNED AUTO_INCREMENT,
size_name VARCHAR(255),
...
PRIMARY KEY(size_id)
);
CREATE TABLE rows (
row_id INTEGER UNSIGNED AUTO_INCREMENT,
...
PRIMARY KEY(row_id)
);
CREATE TABLE columns (
col_id INTEGER UNSIGNED AUTO_INCREMENT,
...
PRIMARY KEY(col_id)
);
CREATE TABLE l_row_size (
row_id INTEGER UNSIGNED,
size_id INTEGER UNSIGNED,
PRIMARY KEY(row_id, size_id),
CONSTRAINT FOREIGN KEY l_row_size-row_id (row_id) REFERENCES rows (row_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT FOREIGN KEY l_row_size-size_id (size_id) REFERENCES sizes (size_id) ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE l_col_size (
col_id INTEGER UNSIGNED,
size_id INTEGER UNSIGNED,
PRIMARY KEY(col_id, size_id),
CONSTRAINT FOREIGN KEY l_col_size-row_id (col_id) REFERENCES rows (col_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT FOREIGN KEY l_col_size-size_id (size_id) REFERENCES sizes (size_id) ON UPDATE CASCADE ON DELETE CASCADE
);
Then:
foreach( $sizes as $size ) {
$dbh->query("INSERT INTO sizes (size_name) VALUES ('{$size['name']}')");
$size_id = $dbh->last_insert_id();
foreach( $rows as $row ) {
$dbh->query("INSERT INTO l_row_size (size_id, row_id) VALUES ($size_id, {$row['id']})");
}
foreach( $cols as $col ) {
$dbh->query("INSERT INTO l_col_size (size_id, col_id) VALUES ($size_id, {$col['id']})");
}
}
edit
Sharf commented:
Each Size Class can have many rows and many columns, but each row and column can only belong to one size class.
Well then you only need 3 tables:
CREATE TABLE sizes (
size_id INTEGER UNSIGNED AUTO_INCREMENT,
...
PRIMARY KEY(size_id)
);
CREATE TABLE rows (
row_id INTEGER UNSIGNED AUTO_INCREMENT,
size_id INTEGER UNSIGNED,
...
PRIMARY KEY(row_id)
CONSTRAINT FOREIGN KEY rows-size_id (size_id) REFERENCES sizes (size_id) ON UPDATE CASCADE ON DELETE CASCADE,
);
CREATE TABLE columns (
col_id INTEGER UNSIGNED AUTO_INCREMENT,
size_id INTEGER UNSIGNED,
...
PRIMARY KEY(col_id),
CONSTRAINT FOREIGN KEY cols-size_id (size_id) REFERENCES sizes (size_id) ON UPDATE CASCADE ON DELETE CASCADE,
);
Linking tables are only truly useful in the case of n:m relationships, and this is a case of two 1:n relationships. Each Row/Column object need only store a reference to the Size to which they belong.

Related

Retrieve primary keys of cascade deleted rows

Good evening,
following table situation:
create table TableA (
pkA int not null auto_increment,
propertyA varchar(20),
primary key (pkA) );
create table TableB (
pkB int not null auto_increment,
pkA int not null,
propertyB varchar(20),
primary key (pkB),
foreign key (pkA) references TableA (pkA) on delete cascade);
create table TableC (
pkC int not null auto_increment,
pkB int not null,
propertyC varchar(20),
primary key (pkC),
foreign key (pkB) references TableB (pkB) on delete cascade );
When I delete a row from parent table 'TableA' I need to know which rows (primary keys) got deleted in 'TableB' and 'TableC' as well.
I am working with PHP 7.0. I was hoping there is something like mysqli_insert_id for deletes, but... nada :(
Any ideas?

Get the value when a MySQL constraint fails

Assuming I am inserting rows in a MySQL table with a constraint
CREATE TABLE `parent` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
;
CREATE TABLE `child` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`id_parent` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
INDEX `FK_parent_child` (`id_parent`),
CONSTRAINT `FK_parent_child` FOREIGN KEY (`id_parent`) REFERENCES `parent` (`id`),
)
ENGINE=InnoDB
;
Then when I do an insert in the child table without the entry in the parent table:
INSERT INTO child (id, id_parent) VALUES (1, 1);
I get the following error:
Cannot add or update a child row: a foreign key constraint fails (`...`.`child`, CONSTRAINT `FK_parent_child` FOREIGN KEY (`id_parent`) REFERENCES `parent` (`id`))`
But is there a way to retrieve the value of the insert-failed row, aka 1 here? Because when I insert thousands of rows at the same time, it would be very useful to get the failed one.
I would like a fully-MySQL way, but a PHP way would work too in my case.

I want to add multiple data in mysql table with php

I have 1 user and I want to insert more than one language(and degree) to him. How can I do this with php?
My languagetype table(all languages inserted)
CREATE TABLE TLANGUAGETYPE (
ID INT NOT NULL AUTO_INCREMENT,
language VARCHAR(100) NOT NULL,
PRIMARY KEY (ID)
)
My language table connected with user table.
CREATE TABLE TLANGUAGE (
languageID INT NOT NULL AUTO_INCREMENT,
userID INT NOT NULL,
ID INT NOT NULL,
degree FLOAT,
PRIMARY KEY (languageID),
FOREIGN KEY (userID) REFERENCES TUSER(userID),
FOREIGN KEY (ID) REFERENCES TLANGUAGETYPE(ID)
)
Instead of adding the languageId in user Table. You can create a mapping table which just contains the "USERID" and "LanguageID" columns.

ID auto increase but not unique

I have a table that I want to have an id that will auto increase itself but not be primary or unique.
Is this possible?
You should really create another table, in that case.
E.g.
CREATE TABLE `Calls` (
`Id` INT(10) AUTO_INCREMENT,
`From` VARCHAR(100) NOT NULL,
`To` VARCHAR(100) NOT NULL,
PRIMARY KEY (`Id`)
) ENGINE=INNODB;
CREATE TABLE `CallHistory` (
`Id` INT(15) AUTO_INCREMENT,
`CallId` INT(10) NOT NULL,
`Text` VARCHAR(255) NOT NULL,
PRIMARY KEY (`Id`),
KEY `CallHistory_Calls_idx` (`CallId`),
CONSTRAINT `CallHistory_Calls`
FOREIGN KEY (`CallId`)
REFERENCES `calls` (`Id`)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=INNODB;
Here's a demo on SQLFiddle.
A benefit of this is that if you delete a row from Calls, the rows in CallHistory will also be deleted.
Running this query:
SELECT `Calls`.`Id`,
`Calls`.`From`,
`Calls`.`To`,
`CallHistory`.`Text`
FROM `Calls`, `CallHistory`
WHERE `Calls`.`Id` = `CallHistory`.`CallId`;
Gives results something like this:
This should work:
id int NOT NULL AUTO_INCREMENT
Anyway I don't see how it wouldn't stay unique unless you update existing values later
Yes, you need to set auto_increment constraint:
CREATE TABLE `test` (
`testID` int(11) NOT NULL, //primary key here
`testInc` int(11) NOT NULL AUTO_INCREMENT, //here is your non-primary auto increment column
PRIMARY KEY (`testID`),
KEY `testInc` (`testInc`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
and if you want this to be unique also then you may add unique constraint:
ALTER TABLE `test` ADD CONSTRAINT ux_unique_constraint UNIQUE `testInc`

MySQL syntax error - can't create a table

here is my code
CREATE TABLE IF NOT EXISTS items
(
id INT NOT NULL AUTO_INCREMENT,
name varchar(256) ,
description TEXT,
price INT ,
images TEXT,
views INT ,
hidden TEXT,
purchases INT,
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS methods
(
method_id INT NOT NULL AUTO_INCREMENT,
method varchar(256),
username varchar(256),
password varchar(256),
PRIMARY KEY (method_id)
);
CREATE TABLE IF NOT EXISTS payments
(
payment_id INT NOT NULL AUTO_INCREMENT,
item_id INT NOT NULL,
method varchar(256),
display INT,
PRIMARY KEY (payment_id) ,
FOREIGN KEY (item_id) REFERENCES items (id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (method) REFERENCES methods (method) ON DELETE CASCADE ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=latin1;
The first 2 tables gets generated ok, but the third one "payments" gives me error code 150 ! which should be related to FK ?!
any help ?
The primary key in the methods table is method_id not method. And the data type is INT not VARCHAR(256)
You need:
CREATE TABLE IF NOT EXISTS payments
(
payment_id INT NOT NULL AUTO_INCREMENT,
item_id INT NOT NULL,
method_id int, -- this is different
display INT,
PRIMARY KEY (payment_id) ,
FOREIGN KEY (item_id) REFERENCES items (id) ON DELETE CASCADE ON UPDATE CASCADE,
-- and this line is different
FOREIGN KEY (method_id) REFERENCES methods (method_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Categories