Insert statement in self-join table - mysql

What is the correct statement to make an insert in a table with a self-join?
If I try to make a classic insert I get the error:
Cannot add or update a child row: a foreign key constraint fails
This is my insert query:
insert into mTable(record_name,self_fk,val, note, ref,insert_date, end_date)
values('processo prova',0,1,'nota di prova', 'az12345', NOW(), NOW());

In your INSERT query, you are referencing to a foreign key (the id 0) that doesn't exist -> constraint fails
I saw in your edits, before you roll it back, your CREATE TABLE script.
The field containing the reference to the parent was created this way :
`id_processo_padre` int(11) NOT NULL DEFAULT '1'
I suggest you to edit this field to make it nullable :
ALTER TABLE `mTable` MODIFY `id_processo_padre` int(11) NULL;
This will allow you to INSERT the first top level parent (or any top level parent)
insert into mTable(record_name, self_fk, ...)
values('processo prova', NULL, ...);
-- ^--^----------This
Test it yourself :
Schema (MySQL v5.7)
CREATE TABLE test
(
id INT(6) NOT NULL PRIMARY KEY AUTO_INCREMENT,
parent_id INT(6) NULL,
someData VARCHAR(255),
FOREIGN KEY (parent_id) REFERENCES test(id)
);
INSERT INTO test VALUES (default, null, "parent"),
(default, 1, "child1_1"),
(default, 1, "child1_2"),
(default, 3, "child2_2");
Query #1
SELECT t1.*, t2.someData AS "My parent's name" FROM test t1
LEFT JOIN test t2
ON t2.id = t1.parent_id
ORDER BY t1.id ASC;
Output
| id | parent_id | someData | My parent's name |
| --- | --------- | -------- | ---------------- |
| 1 | null | parent | null |
| 2 | 1 | child1_1 | parent |
| 3 | 1 | child1_2 | parent |
| 4 | 3 | child2_2 | child1_2 |
View on DB Fiddle

Related

Implicitly insert unique records into foreign key table on insert

Is it possible to add into or remove type_name entries from the type table when a record is inserted in the language table?
I am receiving a #1452 (foreign key constraint) error:
Cannot add or update a child row:
a foreign key constraint fails (`test`.`language`,
CONSTRAINT `language_ibfk_1`
FOREIGN KEY (`type_name`)
REFERENCES `type` (`type_name`)
ON DELETE CASCADE
ON UPDATE CASCADE)
Table Schema
CREATE TABLE IF NOT EXISTS `type` (
`type_name` VARCHAR(128) NOT NULL UNIQUE,
PRIMARY KEY(`type_name`)
);
CREATE TABLE IF NOT EXISTS `language` (
`language_id` INT NOT NULL AUTO_INCREMENT,
`language_name` VARCHAR(256) NOT NULL,
`type_name` VARCHAR(128) NOT NULL,
PRIMARY KEY(`language_id`),
FOREIGN KEY (`type_name`)
REFERENCES `type`(`type_name`)
ON DELETE CASCADE
ON UPDATE CASCADE
);
Insert Statements
INSERT INTO `language`(`language_name`, `type_name`) VALUES
('C', 'programming'),
('Java', 'programming'),
('Python', 'scripting'),
('PHP', 'scripting'),
('HTML', 'markup'),
('XML', 'markup');
Implicitly inserted values due to CASCADE.
INSERT INTO `type`(`type_name`) VALUES
('programming'),
('scripting'),
('markup');
If you really have to use such a solution, you could use a trigger before insert on language table:
CREATE TRIGGER trigger_name BEFORE INSERT ON language FOR EACH ROW
BEGIN
INSERT IGNORE INTO type
(type_name)
VALUES
(NEW.type_name);
END
So, using piotrgajow's response, I have come up with a way to normalize my table by adding a type_index to the type table.
I converted his trigger to a MariaDB/MySQL trigger:
DELIMITER $$
CREATE TRIGGER `insert_language_type`
BEFORE INSERT ON `language`
FOR EACH ROW
BEGIN
INSERT IGNORE INTO `type`
(`type_name`)
VALUES
(NEW.`type_name`);
END;$$
DELIMITER ;
I took this a step further and created a way to re-index the types by creating a type_id column and associating it with the language table. And, in the process, dropping the type_name column from the language table.
/** Add the new type_id index to both tables. */
ALTER TABLE `type` ADD `type_id` INT FIRST;
ALTER TABLE `language` ADD `type_id` INT;
/** Index the type_id values. */
SET #i = 0;
UPDATE `type` SET `type_id`=(#i:=#i+1);
/** Apply the new type_id values to the languages. */
UPDATE `language` L, `type` T
SET L.`type_id` = T.`type_id`
WHERE L.`type_name` = T.`type_name`;
/** Remove all constraints and drop the type_name column. */
ALTER TABLE `type` DROP PRIMARY KEY;
ALTER TABLE `language` DROP FOREIGN KEY `language_ibfk_1`;
ALTER TABLE `language` DROP COLUMN `type_name`;
/** Set primary key for type and add constraint to the language. */
ALTER TABLE `type` MODIFY COLUMN `type_id` INT AUTO_INCREMENT PRIMARY KEY;
ALTER TABLE `language` ADD FOREIGN KEY (`type_id`) REFERENCES `type`(`type_id`);
/** Remove the trigger, because it is meaningless. */
DROP TRIGGER `insert_language_type`;
Before
+-------------------------------------------+ +-------------+
| language | | type |
+-------------+---------------+-------------+ +-------------+
| language_id | language_name | type_name | | type_name |
+-------------+---------------+-------------+ +-------------+
| 1 | C | programming | | markup |
| 2 | Java | programming | | programming |
| 3 | Python | scripting | | scripting |
| 4 | PHP | scripting | +-------------+
| 5 | HTML | markup |
| 6 | XML | markup |
+-------------+---------------+-------------+
After
+---------------------------------------+ +-------------------- --+
| language | | type |
+-------------+---------------+---------+ +---------+-------------+
| language_id | language_name | type_id | | type_id | type_name |
+-------------+---------------+---------+ +---------+-------------+
| 1 | C | 2 | | 1 | markup |
| 2 | Java | 2 | | 2 | programming |
| 3 | Python | 3 | | 3 | scripting |
| 4 | PHP | 3 | +---------+-------------+
| 5 | HTML | 1 |
| 6 | XML | 1 |
+-------------+---------------+---------+

InnoDB hierarchical data: recursively delete fragment of a tree

This is my table:
CREATE TABLE `pages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent` int(11) DEFAULT NULL,
`label` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
where id is a unique id (autoincrement) and parent is also an id from the same table. If parent is NULL, the page hasn't got parent.
What I want? If I delete one parent, it should auto delete all his childs in the same table. I believe that this can be done using on DELETE CASCADE, and this is the way that I want to do it :). But I've tried many configurations of code, and none of them work. Either table cannot be created, or insert query is not working, because of an error that looks similar to "key not exist".
What I found?
How to recursively delete items from table? - answer is great, but with none of code. This answer to the same question:
https://stackoverflow.com/a/9260373/1125465 doesn't work for me. There are some problems with table creation. I think this answer was made in a hurry, and there is some key word missing?
Recursive MySQL Query with relational innoDB this one is simmilar, but not the same case, there are few tables.
Sql server - recursive delete - Come on, there must be a simpliest answer...
Technical info:
mysql Ver 14.14 Distrib 5.1.70, for pc-linux-gnu (x86_64) using
readline 5.1
Table engine: InnoDB
This works for me (and here's a fiddle http://sqlfiddle.com/#!8/d15b4/1):
CREATE TABLE
test
(
id INT NOT NULL PRIMARY KEY,
parent INT,
CONSTRAINT
fk_test_test
FOREIGN KEY
(parent)
REFERENCES
test (id)
ON DELETE CASCADE
);
INSERT
INTO test
VALUES (1, NULL),
(2, 1),
(3, 2),
(4, 3),
(5, NULL),
(6, 5);
SELECT *
FROM test;
+----+--------+
| id | parent |
+----+--------+
| 1 | NULL |
| 5 | NULL |
| 2 | 1 |
| 3 | 2 |
| 4 | 3 |
| 6 | 5 |
+----+--------+
DELETE
FROM test
WHERE id = 1;
SELECT *
FROM test;
+----+--------+
| id | parent |
+----+--------+
| 5 | NULL |
| 6 | 5 |
+----+--------+

Foreign Key Failure

Ok, so I've been trying to figure out why I keep getting this specific error. MySql keeps giving me error code 1452. Cannot add or update a child row. My tables are as such.
CREATE TABLE IF NOT EXISTS `ecommerce`.`departments` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(100) NOT NULL ,
PRIMARY KEY (`id`) )
ENGINE = MyISAM;
INSERT INTO `ecommerce`.`departments`
VALUES (1, 'Development'), (2, 'Marketing'),
(3, 'Sales'), (4, 'Customer Service');
CREATE TABLE IF NOT EXISTS `ecommerce`.`department_roles` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(100) NOT NULL ,
`map` VARCHAR(255) NOT NULL ,
`parent_id` INT NOT NULL ,
PRIMARY KEY (`id`) )
ENGINE = MyISAM;
INSERT INTO `ecommerce`.`department_roles`
VALUES (1, 'Admin', '/admin', 0), (2, 'Create', '/admin', 1),
(3, 'Update', '/admin', 1), (4, 'Delete', '/admin', 1);
CREATE TABLE IF NOT EXISTS `ecommerce`.`department_roles_map` (
`id` INT NOT NULL AUTO_INCREMENT ,
`department_roles_id` INT NOT NULL ,
`departments_id` INT NOT NULL ,
PRIMARY KEY (`id`) ,
INDEX `fk_drm_departments` (`departments_id` ASC) ,
INDEX `fk_drm_department_roles` (`department_roles_id` ASC) ,
CONSTRAINT `fk_drm_departments`
FOREIGN KEY (`departments_id` )
REFERENCES `ecommerce`.`departments` (`id` )
ON DELETE CASCADE
ON UPDATE NO ACTION,
CONSTRAINT `fk_drm_department_roles`
FOREIGN KEY (`department_roles_id` )
REFERENCES `ecommerce`.`department_roles` (`id` )
ON DELETE CASCADE
ON UPDATE NO ACTION)
ENGINE = InnoDB;
Now, when I try to select from departments and department_roles, I show data.
SELECT * FROM department_roles;
+----+--------+--------+-----------+
| id | name | map | parent_id |
+----+--------+--------+-----------+
| 1 | Admin | /admin | 0 |
| 2 | Create | /admin | 1 |
| 3 | Update | /admin | 1 |
| 4 | Delete | /admin | 1 |
+----+--------+--------+-----------+
4 rows in set (0.00 sec)
SELECT * FROM departments;
+----+--------+--------+-----------+
| id | name | map | parent_id |
+----+--------+--------+-----------+
| 1 | Admin | /admin | 0 |
| 2 | Create | /admin | 1 |
| 3 | Update | /admin | 1 |
| 4 | Delete | /admin | 1 |
+----+--------+--------+-----------+
4 rows in set (0.00 sec)
But, when I try to insert into department_roles_map, I get this.
INSERT INTO department_roles_map(department_roles_id, departments_id) VALUES (1, 1);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`ecommerce`.`department_roles_map`, CONSTRAINT `fk_drm_departments` FOREIGN KEY (`departments_id`) REFERENCES `departments` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION)
Any help would be much appreciated!
Firstly, I'm impressed that you managed to create an InnoDB table that has FK references to two MyISAM tables!
Try creating all three table with InnoDB engine and trying again....
Both the parent and the child tables need to use the InnoDB storage engine, but you're using MyISAM for the parent tables.
My guess is that there is already a table named department_roles_map, so when you runCREATE TABLE IF NOT EXISTS it's failing because the table already exists, and ignoring the error. Then when you try to insert data into the other department_roles_map, it fails with the FK error.
But that's just a guess.
I agree with Tom Mac, try creating all 3 tables using InnoDB, but you should also confirm that no other tables with those names already exists.

MySQL update to other tables

I want to be able to insert data into t1 and have data get populated in table t2 with the primary key as a foreign key in t2.
Basically, how come in my current setup when I INSERT INTO t1 (first_name, last_name) values ( "blah", "blah"); and then do SELECT * FROM t2; t2 it says Empty Set (0.00 sec) for t2? Shouldn't it at least show the default id of 1?
t1:
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| first_name | varchar(20) | NO | | NULL | |
| last_name | varchar(20) | NO | | NULL | |
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
+------------+------------------+------+-----+---------+----------------+
t2:
+-----------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+-------+
| address | varchar(50) | NO | | NULL | |
| id | int(10) unsigned | NO | MUL | NULL | |
| last_name | varchar(20) | YES | | NULL | |
+-----------+------------------+------+-----+---------+-------+
In a relational database, a FOREIGN KEY is a declaration that you intend to insert values into T2 that must match an already existing value in T1, and that you want the database to refuse to perform any action that would break this relationship.
It does not mean that the database will create records on its own in order to satisfy a relationship. If you try to insert a value into T2 that does not exist in T1, the command will fail; it will not add the required record to T1.
That is the opposite of what you're suggesting, however, in which you want the foreign key values to get automatically generated. However, there's no requirement that a primary key value actually have references and, furthermore, no limit on the number of times that primary key value can be referenced — so how would the database guess what should be created in T2?
That said, if you want some of your own code to execute automatically when data is added to T1, code which can do whatever you want, you can create a trigger on T1.
No, tables won't propagate automatically. (You can however do it with triggers) You will have to insert into t2.
You can create a trigger on table t1 so that it inserts a row into t2 with the correct id and the other fields NULL
Foreign keys will not insert records for you.
DELIMITER ;;
CREATE TRIGGER insert_addr_rec BEFORE INSERT ON t1
FOR EACH ROW BEGIN
INSERT INTO t2 SET id=NEW.id, last_name=NEW.last_name
END ;;
DELIMITER ;
NB untested code

MySQL INSERT if one of primary key value doesn't match

I would like to do MySQL query like this:
if((value1 != dbPrimaryValue1) OR (value2 != dbPrimaryValue2))
INSERT ROW
else
DO NOTHING
Lets try example:
CREATE TABLE `tmp` (
`one` int NOT NULL,
`two` int NOT NULL,
`three` int NOT NULL);
ALTER TABLE `tmp`
ADD PRIMARY KEY (`one`, `two`);
INSERT INTO `tmp`
(`one`, `two`, `three`)
VALUES (1,2,3);
INSERT INTO `tmp`
(`one`,`two`,`three`)
VALUES (10,20,30),
(1,999,999),
(999,2,999),
(1,2,999)
ON DUPLICATE KEY
UPDATE `one` = `one`; // or some dummy no-source-drain operation
Result is here:
select * from tmp;
+-----+-----+-------+
| one | two | three |
+-----+-----+-------+
| 1 | 2 | 3 |
| 10 | 20 | 30 |
| 1 | 999 | 999 |
| 999 | 2 | 999 |
+-----+-----+-------+
U would like to have result like this:
select * from tmp;
+-----+-----+-------+
| one | two | three |
+-----+-----+-------+
| 1 | 2 | 3 |
| 10 | 20 | 30 |
+-----+-----+-------+
Is it possible to make this query? I'm operating with huge data and procedure like load -> compare -> save is not possible. THANKS!
Simply make both fields unique separately. For example:
CREATE TABLE `tmp` (
`one` int NOT NULL UNIQUE,
`two` int NOT NULL UNIQUE,
`three` int NOT NULL);
Or add the constraints via:
ALTER TABLE `tmp` ADD UNIQUE (`one`);
ALTER TABLE `tmp` ADD UNIQUE (`two`);
If you create a UNIQUE key constraint, the database will not allow you to insert them automatically.
From MySQL forum:
A UNIQUE index creates a constraint
such that all values in the index must
be distinct. An error occurs if you
try to add a new row with a key value
that matches an existing row. For all
engines, a UNIQUE index permits
multiple NULL values for columns that
can contain NULL.