I get "a foreign key constraint fails" error when attempting to join two classes/tables - sql

I have these classes, abbreviated for practical reasons:
class CV {
Date dateCreated
static hasMany=[proposals: Proposal]
}
class Proposal {
String name
Date date_started
static hasMany = [CVs: CV]
static belongsTo = CV
}
Grails creates tables for both these classes, and a third class named "cv_proposals" joining them. So far, so good. I have data in both the CV and the Proposal tables, they both have autoincremented "id" values. All good.
in Oracle MySQL Workbench, I try to manually add values to the joining table to get some dummy data to work with. I get an error message with this trace:
ERROR 1452: Cannot add or update a child row: a foreign key constraint fails
(cvreg_utv.cv_proposals, CONSTRAINT FK17D946F55677A672 FOREIGN KEY (cv_id) REFERENCES cv (id))
I made sure both the tables had several lines of data in them, and that I could edit both of them separately.
After trying dropping and recreating the table, altering the classes back and forth, I'm kind of convinced that this operation somehow has to be done through a running Grails application. So I write this script in a controller and run it:
def g = CV.get(1)
Proposal proposal = g.addToProposals(new Proposal(
name: "SavingTest",
date_started: new Date())).save()
I still get the same error, though. Is this not the right way to define a proposal that is connected to a certain CV? Am I wrong in using a many-to-many connection here somehow?
Edit: adding the schema-create script for the joining table
delimiter $$
CREATE TABLE `cv_proposals` (
`proposal_id` bigint(20) NOT NULL,
`cv_id` bigint(20) NOT NULL,
PRIMARY KEY (`cv_id`,`proposal_id`),
KEY `FK17D946F55677A672` (`cv_id`),
KEY `FK17D946F5F7217832` (`proposal_id`),
CONSTRAINT `FK17D946F5F7217832` FOREIGN KEY (`proposal_id`) REFERENCES `proposal` (`id`),
CONSTRAINT `FK17D946F55677A672` FOREIGN KEY (`cv_id`) REFERENCES `cv` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$
And the CV table:
CREATE TABLE `cv` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`version` bigint(20) NOT NULL,
`user_id` bigint(20) NOT NULL,
`version_name` varchar(255) DEFAULT NULL,
`date_created` datetime NOT NULL,
`last_updated` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `FKC734A9AB992` (`user_id`)
) ENGINE=MyISAM AUTO_INCREMENT=101 DEFAULT CHARSET=latin1$$
And the Proposal table:
CREATE TABLE `proposal` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`version` bigint(20) NOT NULL,
`date_ended` datetime NOT NULL,
`date_started` datetime NOT NULL,
`description` varchar(500) DEFAULT NULL,
`name` varchar(500) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1$$
This is the insert script I tried to run:
INSERT INTO `cvreg_utv`.`cv_proposals` (`proposal_id`, `cv_id`)
VALUES ('1', '1');

You crated the tables manually? It's interesting that cv table is using MyISAM engine and the others uses InnoDB.
I think you want to use InnoDB to all your tables, since this engine is transactional. In my test, I also was unable to create the cv_proposals table until I changed the cv creation:
CREATE TABLE cv (
id bigint(20) NOT NULL AUTO_INCREMENT,
version bigint(20) NOT NULL,
user_id bigint(20) NOT NULL,
version_name varchar(255) DEFAULT NULL,
date_created datetime NOT NULL,
last_updated datetime NOT NULL,
PRIMARY KEY (id),
KEY FKC734A9AB992 (user_id)
) ENGINE=InnoDB AUTO_INCREMENT=101
After that, the insert's worked smoothly.

Related

Foreign key pointing to a primary key which is also part of an unique key

I created a database using MariaDb. I have the following author table (with id as primary key), which is extended by two other tables, author_personal and author_corporate, representing two different kinds of author and having different fields:
CREATE TABLE `author` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`author_type` char(1) NOT NULL,
-- other fields
PRIMARY KEY (`id`),
UNIQUE KEY `author_UN` (`id`,`author_type`) USING BTREE,
CONSTRAINT `author_CHECK_type` CHECK (`author_type` in ('P','C'))
);
CREATE TABLE `author_personal` (
`id` int(10) unsigned NOT NULL,
`surname` varchar(30) DEFAULT NULL,
`name` varchar(30) DEFAULT NULL,
-- other fields
`author_type` char(1) GENERATED ALWAYS AS ('P') VIRTUAL,
PRIMARY KEY (`id`),
KEY `author_personal_FK` (`id`,`author_type`),
CONSTRAINT `author_personal_FK` FOREIGN KEY (`id`, `author_type`) REFERENCES `author` (`id`, `author_type`) ON DELETE CASCADE
);
CREATE TABLE `author_corporate` (
`id` int(10) unsigned NOT NULL,
`corporate_name` varchar(50) DEFAULT NULL,
`corporate_acronym` varchar(5) DEFAULT NULL,
`author_type` char(1) GENERATED ALWAYS AS ('C') VIRTUAL,
PRIMARY KEY (`id`),
KEY `author_corporate_FK` (`id`,`author_type`),
CONSTRAINT `author_corporate_FK` FOREIGN KEY (`id`, `author_type`) REFERENCES `author` (`id`, `author_type`) ON DELETE CASCADE
);
Although author.id would be enough, I decided to create an UNIQUE KEY (id, author_type) to be referenced by the foreign key in the two other tables, so that it would be impossible to reference from author_personal an author which is not flagged as P, and from author_corporate an author which is not flagged as C.
The problem rises when I want to reference author using the primary key, like in this table:
CREATE TABLE `work_authors` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`work_id` int(10) unsigned NOT NULL,
`author_id` int(10) unsigned NOT NULL,
-- other fields
PRIMARY KEY (`id`),
KEY `work_authors_FK` (`work_id`),
KEY `work_authors_FK_author` (`author_id`) USING BTREE,
CONSTRAINT `work_authors_FK` FOREIGN KEY (`work_id`) REFERENCES `work` (`id`) ON UPDATE CASCADE,
CONSTRAINT `work_authors_FK_author` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`) ON UPDATE CASCADE
);
Both DBeaver and PhpMyAdmin think that work_authors_FK_author references author_UN instead of the actual primary key (author.id).
This means that I am not able to click on the values in work_authors.author_id and open the referenced record in authors because I get the following error:
Entity [davide_library.author] association [work_authors_FK_author] columns differs from referenced constraint [author_UN] (1<>2)
I created the foreign keys using DBeaver. Although I selected PRIMARY as the unique key, in the list of foreign keys it always show author_UN as the referenced object. I don't know if this behavior depends on MariaDB or DBeaver.
My question is:
Is there any way to explicitily reference in the DDL the primary key instead of the unique key?
or, alternatively:
Is there a different way, other than the UNUQUE constraint, to check that author_personal references only authors flagged with P, and the same for author_corporate?

Creating relationship between primary key to composite key -- phpMyAdmin

I am trying to create a relation between a field 'fldEmpID' in my Employees table to a foreign composite key which consists of fldEmpID and fldEventID but it is not allowing the relation to be created. I don't understand why this relation won't work, I was able to create a similar relation between fldEventID from an Events to the composite key. Both fldEmpID fields in each table are int(11). What can I do to create this relation?
The following are the two tables... (I would like to keep the composite key on the table to the right as it helps to prevent duplicates and works well)
It seems to work as expected for me. I created the tables and used the Designer tab to create the relation (by selecting the "Create relation" icon, then clicking fldEmpId in table a, and finally selecting fldEmpID in table b).
For reference, I pasted below the structure of my table (which includes the keys and restraints)
CREATE TABLE IF NOT EXISTS `a` (
`fldEmpId` int(11) NOT NULL,
`fldEmpName` varchar(50) NOT NULL,
`fldEmail` varchar(50) NOT NULL,
`fldPassHash` varchar(50) NOT NULL,
`fldPassSalt` varchar(50) NOT NULL,
`fldAdmin` enum('1','2') NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `b` (
`fldEmpID` int(11) NOT NULL,
`fldEventID` bigint(20) unsigned NOT NULL,
`fldDTAdded` datetime NOT NULL,
`fldDTRemoved` datetime NOT NULL,
`fldPosition` enum('0','1','2','3','4','5') NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `a`
ADD PRIMARY KEY (`fldEmpId`);
ALTER TABLE `b`
ADD PRIMARY KEY (`fldEmpID`), ADD UNIQUE KEY `fldEventID` (`fldEventID`);
ALTER TABLE `a`
MODIFY `fldEmpId` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `b`
ADD CONSTRAINT `fk` FOREIGN KEY (`fldEmpID`) REFERENCES `a` (`fldEmpId`);

How i can make tables for the given many to many related classes?

I have a scenario of of three classes .I am planning to make the database for it .The relationship between them is :
1 customer is related with many items and many dv-vouchers.
1 item is related with many customer and many dv-vouchers.
1 dv-vouchers is related with 1 customer and many items .
public Customer
{
int cust_id;
list<items> items;
list<dvouchers> dvouchers;
}
public items
{
int itm_id;
list<Customer> customers;
list<dvouchers> dvouchers;
}
public dvouchers
{
int dv_id;
Customer customer;
list<items> items;
}
First of all what can be the design for database tables for above classes, fk_constraints and relationship tables ?
Second Do I need to perform Insert and Update operation on both the relationship tables along db table ? Please Help .
1.
You need two tables Customer_Item and Item_Dvoucher for relationship many-to-many and field cust_id in table Dvoucher for one-to-one relationship.
2.
You need to insert or update data in base tables Customer, Item and Dvoucher. Then you need to add or remove relationship in table Customer_Item and Items_Dvoucher. Also fill field cust_id in table Dvoucher.
Generated in mysql:
CREATE TABLE `Customer` (
`cust_id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`cust_id`)
)
CREATE TABLE `Dvoucher` (
`dv_id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) DEFAULT NULL,
`cust_id` INT(11) DEFAULT NULL,
PRIMARY KEY (`dv_id`),
KEY `cust_id` (`cust_id`),
CONSTRAINT `Dvoucher_ibfk_1` FOREIGN KEY (`cust_id`) REFERENCES `Customer` (`cust_id`)
)
CREATE TABLE `Item` (
`item_id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`item_id`)
)
CREATE TABLE `Customer_Item` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`cust_id` INT(11) DEFAULT NULL,
`item_id` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `cust_id` (`cust_id`),
KEY `item_id` (`item_id`),
CONSTRAINT `Customer_Item_ibfk_1` FOREIGN KEY (`cust_id`) REFERENCES `Customer` (`cust_id`),
CONSTRAINT `Customer_Item_ibfk_2` FOREIGN KEY (`item_id`) REFERENCES `Item` (`item_id`)
)
CREATE TABLE `Item_Dvoucher` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`item_id` INT(11) DEFAULT NULL,
`dv_id` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `item_id` (`item_id`),
KEY `dv_id` (`dv_id`),
CONSTRAINT `Item_Dvoucher_ibfk_1` FOREIGN KEY (`item_id`) REFERENCES `Item` (`item_id`),
CONSTRAINT `Item_Dvoucher_ibfk_2` FOREIGN KEY (`dv_id`) REFERENCES `Dvoucher` (`dv_id`)
)
In SQLite:
CREATE TABLE customer(cust_id INTEGER PRIMARY KEY, ...);
CREATE TABLE item(itm_id INTEGER PRIMARY KEY, ...);
CREATE TABLE dvoucher(dv_id INTEGER PRIMARY KEY, cust_id REFERENCES customer(cust_id), ...);
CREATE TABLE dvoucher_item(dv_id REFERENCES dvoucher(dv_id), itm_id REFERENCES item(itm_id));
CREATE TABLE customer_item(cust_id INTEGER PRIMARY KEY, itm_id REFERENCES item(itm_id));
As #Andrei solution to MySQL, using dvoucher.cust_id to link dvoucher to costumer, and tables dvoucher_item and customer_item to many to many links.

Specify sorting criteria manually for a set of records of a many-to-many relationship

I am designing a many-to-many relationship between two models: Cell and Isolator:
CREATE TABLE `isolator` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(127) NOT NULL,
`surname` varchar(127) NOT NULL,
PRIMARY KEY (`id`),
);
CREATE TABLE `cell` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(127) NOT NULL,
`gen` varchar(127) NOT NULL,
PRIMARY KEY (`id`),
);
CREATE TABLE `cell_isolators` (
`cell_id` bigint(20) NOT NULL DEFAULT '0',
`isolator_id` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`cell_id`,`isolator_id`),
CONSTRAINT `cell_isolator_id` FOREIGN KEY (`isolator_id`) REFERENCES `isolator` (`id`),
CONSTRAINT `isolator_cell_id` FOREIGN KEY (`cell_id`) REFERENCES `cell` (`id`) ON DELETE CASCADE
);
The client has asked me the possibility of specifying the order on which the list of isolators for a cell is shown, since it's important for publication purposes.
What is the best approach to model that? I was thinking on adding a third field to the many-to-many relation (e.g. sort_order), but I would like to know if there are other alternatives.
Thanks!
The only way to do that in the general case is to add a column to "cell_isolators".
The data type is application-dependent to a certain extent. I've seen this done with columns as integers, floats or decimals, and alphanumerics (varchar(n)).
Populating and maintaining it can be troublesome, though. That doesn't have anything to do with the database design; it's just painful to maintain the sort order for more than a few rows, especially if there are regular inserts to the table and changes to the sort order. Fortunately, that job usually falls to the user interface, not to the dbms.

Two n x m relationships with the same table in mysql

I want to create a database in which there's an n x m relationship between the table drug and the table article and an n x m relationship between the table target and the table article.
I get the error: Cannot delete or update a parent row: a foreign key constraint fails
What do I have to change in my code?
DROP TABLE IF EXISTS `textmine`.`article`;
CREATE TABLE `textmine`.`article` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Pubmed ID',
`abstract` blob NOT NULL,
`authors` blob NOT NULL,
`journal` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `textmine`.`drugs`;
CREATE TABLE `textmine`.`drugs` (
`id` int(10) unsigned NOT NULL COMMENT 'This ID is taken from the biosemantics dictionary',
`primaryName` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `textmine`.`targets`;
CREATE TABLE `textmine`.`targets` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`primaryName` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `textmine`.`containstarget`;
CREATE TABLE `textmine`.`containstarget` (
`targetid` int(10) unsigned NOT NULL,
`articleid` int(10) unsigned NOT NULL,
KEY `target` (`targetid`),
KEY `article` (`articleid`),
CONSTRAINT `article` FOREIGN KEY (`articleid`) REFERENCES `article` (`id`),
CONSTRAINT `target` FOREIGN KEY (`targetid`) REFERENCES `targets` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `textmine`.`contiansdrug`;
CREATE TABLE `textmine`.`contiansdrug` (
`drugid` int(10) unsigned NOT NULL,
`articleid` int(10) unsigned NOT NULL,
KEY `drug` (`drugid`),
KEY `article` (`articleid`),
CONSTRAINT `article` FOREIGN KEY (`articleid`) REFERENCES `article` (`id`),
CONSTRAINT `drug` FOREIGN KEY (`drugid`) REFERENCES `drugs` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
You are trying to create tables out of order.
For example you are trying to create contiansdrug table which refers to table drugs before drugs table.
Remember that any SQL, even DDL, tries to leave database in consistent state.
I would recommend putting the commands in proper order. Alternatively you have options to turn off the checks temporarily and run the creation scrip inside transaction, see the instructions here
Relevant section is
SET AUTOCOMMIT = 0;
SET FOREIGN_KEY_CHECKS=0;
.. your script..
SET FOREIGN_KEY_CHECKS = 1;
COMMIT;
SET AUTOCOMMIT = 1;
EDIT:
OK, try not to have same names for constraints. Reading the fine manual enlightens:
If the CONSTRAINT symbol clause is
given, the symbol value must be unique
in the database. If the clause is not
given, InnoDB creates the name
automatically.
EDIT2:
To spell it out, you have duplicate constrain symbol article, rename it and all will be fine.
Standard practice is if you name your constrains to use names that describe what is related, for example containsdrug_acticelid_article_id (firsttablename_column_secondtablename_column) would be unique and descriptive.
I solved the problem by not declaring the Foreign Key inside of MySql but simply declaring them as ints.