I am migrating a table to a new format and as part of the process, I need to delete rows and condense them into a single row. However, these old rows are being referenced by other tables.
Is there a way to automatically update the values of the foreign keys referencing the old rows to the 'id' of the newly-created row or is this only possible manually going through all the referencing tables? I am using a number (id) to identify the rows.
The only other way possible is to go through each referencing table and update the old values with multiple sub-queries OR disable foreign key checks then delete the old rows, but this wouldn't update the values in child tables.
Error Message: SQL Error (1451): Cannot delete or update a parent row:
a foreign key constraint fails... (table names I can't show)
I suggest you to create a mapping between the Old and New Ids first, and then temporary introduce a new field called NewId what you will populate from that mapping. Next, once you have populated all new Ids, you can simply update your primary Id from the new one and delete the NewId column completely.
Of course, before you start any key updates you will require to disable a foreign key constraint, read this post about how to do it.
I have a problem with this:
Database diagram
object_id is a foreign key references 5 tables' "id" columns.
So I can not insert for example 5th record to "connected_nodes" table because in "klapan_treh" table 5th record does not exist, but in "ns" table 5th record exists.
My solution is to create for each table separate columns like: ns_id references ns(id), klapan_treh_id references klapan_treh(id) etc.
But do you advise me another improved way?
Your proposed solution is the best one, I think. What you need is to have 5 separate foreign keys in your connected_nodes table. Each one of these points to the id in exactly one of the other tables. I think that is what you are suggesting.
Each foreign key can be optional (nullable) so that if you have 4 connected nodes you have 4 of the foreign keys filled in and the 5th one null.
I have a table with 2 foreign keys that reference the same field within another table. I know how to define the foreign key constraint to remove my table entry if at least one of the two foreign keys become deleted. But instead I want to keep the table entry if at least one of the foreign keys still exist?
CREATE TABLE PrivateMessages
...
INDEX(FromEmail, ToEmail),
FOREIGN KEY(FromEmail, ToEmail)
REFERENCES Users(Email, Email)
ON UPDATE CASCADE
ON DELETE CASCADE,
...
The table stores messages between two users. I want to delete messages if both users don't exist any longer, only. Maybe, is there a better approach to realize this?
... I want to keep the table entry if at least one of the foreign keys still exist
I want to delete messages if both users don't exist any longer, only.
It can't be achieved just by defining a constraint.
Possible procedure:
Define a marked_for_delete bit not null default 0 column feature.
Whenever you want to delete a user, mark it for deletion, by an
update call.
Define either a before or an after type of delete trigger on
the table.
In the trigger body, check if the combo of fromEmail and toEmail
are marked for deletion, then execute a delete statement on the
parent table and the child combo will be deleted as your required
condition matched.
How would you implement the following?
I would like to insert data to mysql tables. Let's imagine there are two tables where a foreign key relation exists. First, i insert a row that has a primary key that should be inserted as a foreign key to one of the rows to the other table. So when i would like to insert the foreign key and it's related data, i have to know the primary key of the related row in the other table. As i am a beginner, my solution would be the following: I would insert a field value with a particular data to the original table so that the inserted value could be used to retrieve the primary key with a SELECT, and then insert the retrieved primary key as the foreign key to the related rows of the other table.
Although I don't know a better solution, I think this would be a very clumsy way to implement this logic. There must be a better way of doing this.
Your solution won't work because if you are inserting not unique data, you may not retrieve the appropriate primary key. MySQL offers LAST_INSERT_ID() function for this. Just insert row into your primary key table and then use SELECT LAST_INSERT_ID(). It returns last primary key value inserted into your original table (last insert query) and now you can use it as foreign key in related table.
I thought a foreign key meant that a single row must reference a single row, but I'm looking at some tables where this is definitely not the case. Table1 has column1 with a foreign key constraint on column2 in table2, BUT there are many records in table2 with the same value in column2. There's also non-unique index on column2. What does this mean? Does a foreign key constraint simply mean that at least one record must exist with the right values in the right columns? I thought it meant there must be exactly one such record (not sure how nulls fit in to the picture, but I'm less concerned about that at the moment).
update: Apparently, this behavior is specific to MySQL, which is what I was using, but I didn't mention it in my original question.
From MySQL documentation:
InnoDB allows a foreign key constraint to reference a non-unique key. This is an InnoDB extension to standard SQL.
However, there is a pratical reason to avoid foreign keys on non-unique columns of referenced table. That is, what should be the semantic of "ON DELETE CASCADE" in that case?
The documentation further advises:
The handling of foreign key references to nonunique keys or keys that contain NULL values is not well defined (...) You are advised to use foreign keys that reference only UNIQUE (including PRIMARY) and NOT NULL keys.
Your analysis is correct; the keys don't have to be unique, and constraints will act on the set of matching rows. Not usually a useful behavior, but situations can come up where it's what you want.
When this happens, it usually means that two foreign keys are being linked to each other.
Often the table that would contain the key as a primary key isn't even in the schema.
Example: Two tables, COLLEGES and STUDENTS, both contain a column called ZIPCODE.
If we do a quick check on
SELECT * FROM COLLEGES JOIN STUDENTS ON COLLEGES.ZIPCODE = STUDENTS.ZIPCODE
We might discover that the relationship is many to many. If our schema had a table called ZIPCODES, with primary key ZIPCODE, it would be obvious what's really going on.
But our schema has no such table. Just because our schema has no such table doesn't mean that such data doesn't exist, however. somewhere, out in USPO land, there is just such a table. And both COLLEGES.ZIPCODE and STUDENTS.ZIPCODE are references to that table, even if we don't acknowledge it.
This has more to do with the philosophy of data than the practice of building databases, but it neatly illustrates something fundamental: the data has characteristics that we discover, and not only characteristics that we invent. Of course, what we discover could be what somebody else invented. That's certainly the case with ZIPCODE.
Yes, you can create foreign keys to basically any column(s) in any table. Most times you'll create them to the primary key, though.
If you do use foreign keys that don't point to a primary key, you might also want to create a (non-unique) index to the column(s) being referenced for the sake of performance.
Depends on the RDBMS you're using. I think some do this for you implicitly, or use some other tricks. RTM.
PostgreSQL also refuses this (anyway, even if it is possible, it does not mean it is a good idea):
essais=> CREATE TABLE Cities (name TEXT, country TEXT);
CREATE TABLE
essais=> INSERT INTO Cities VALUES ('Syracuse', 'USA');
INSERT 0 1
essais=> INSERT INTO Cities VALUES ('Syracuse', 'Greece');
INSERT 0 1
essais=> INSERT INTO Cities VALUES ('Paris', 'France');
INSERT 0 1
essais=> INSERT INTO Cities VALUES ('Aramits', 'France');
INSERT 0 1
essais=> INSERT INTO Cities VALUES ('Paris', 'USA');
INSERT 0 1
essais=> CREATE TABLE People (name TEXT, city TEXT REFERENCES Cities(name));
ERROR: there is no unique constraint matching given keys for referenced table "cities"
Necromancing.
As others already said, you shouldn't reference a non-unique key as foreign key.
But what you can do instead (without delete cascade danger) is adding a check-constraint (at least in MS-SQL).
That's not exactly the same as a foreign key, but at least it will prevent the insertion of invalid/orphaned/dead data.
See here for reference (you'll have to port the MS-SQL code to MySQL syntax):
Foreign Key to non-primary key
Edit:
Searching for the reasons for the downvote, according to Mysql CHECK Constraint, MySQL doesn't really support CHECK constraints.
You can define them in your DDL query for compatibility reasons, but they are just ignored...
But as mentioned there, you can create a BEFORE INSERT and BEFORE UPDATE trigger, which will throw an error when the requirements of the data are not met, which is basically the same thing, except that it's an even bigger mess.
As to the question:
I thought a foreign key meant that a single row must reference a
single row, but I'm looking at some tables where this is definitely
not the case.
In any sane RDBMS, this is true.
The fact that this is possible in MySQL is just one more reason why
MySQL is an in-sane RDBMS.
It may be fast, but sacrificing referential integrity and data quality on the altar of speed is not my idea of a quality-rdbms.
In fact, if it's not ACID-compliant, it's not really a (correctly functioning) RDBMS at all.
What database are we talking about? In SQL 2005, I cannot create a foreign key constraint that references a column that does not have a unique constraint (primary key or otherwise).
create table t1
(
id int identity,
fk int
);
create table t2
(
id int identity,
);
CREATE NONCLUSTERED INDEX [IX_t2] ON [t2]
(
[id] ASC
);
ALTER TABLE t1 with NOCHECK
ADD CONSTRAINT FK_t2 FOREIGN KEY (fk)
REFERENCES t2 (id) ;
Msg 1776, Level 16, State 0, Line 1
There are no primary or candidate keys in the referenced table 't2'
that match the referencing column list in the foreign key 'FK_t2'.
Msg 1750, Level 16, State 0, Line 1
Could not create constraint. See previous errors.
If you could actually do this, you would effectively have a many-to-many relationship, which is not possible without an intermediate table. I would be truly interested in hearing more about this ...
See this related question and answers as well.