MYSQL Update all foreign key values - mysql

I have two identical tables that are located in two identical databases(with different name). I want to merge these two tables, but their primary keys are used in other tables,
these tables look like this:
Table A
id column1 column2 column3
___ ________ _______ ________
1 text text text
2 text text text
3 text text text
Table B
id column1 column2 column3
___ ________ _______ ________
2 text text text
3 text text text
4 text text text
tables that are linked to Table A
Link A
id column1 tableA_ID
___ ________ _______
1 text 2
2 text 3
3 text 4
Link B
id column1 tableA_ID
___ ________ _______
1 text 3
2 text 3
3 text 2
Please note, the tables have identical id's, this means when I do the merge, I have to change the id's of the second table. Remember the second table's primary keys are used in other tables.
I wrote this query to merge the two tables:
INSERT INTO db_A.`Table_A`(`column2`,`column3`)
SELECT `column2`,`column3` FROM db_B.`Table_B`;
This query will correctly copy the records of the second table to the first table.
Now I want to also move the data of the tables that are linked with Table B, I can use the same query, but now the foreign key will not match, because the ID they were linked with has been changed.
How do I update them so that the ID will match again?
NB: I do not have the ON UPDATE CASCADE constraint on those tables
I hope this make sense, I will try to improve this question so that everyone understands it.
Database Info
Type : MySQL
Engine: MyISAM

You can apply ON UPDATE CASCADE to each table with foreign keys related to TableB.id in second database temporary:
ALTER TABLE db2.other_tables_with_fk DROP FOREIGN KEY fk_to_TableB;
ALTER TABLE db2.other_tables_with_fk
ADD CONSTRAINT fk_to_TableB FOREIGN KEY (TableB_id)
REFERENCES TableB(id) ON UPDATE CASCADE;
and afterwards use the trick in Sami's Answer and then remove temporary changes like this:
ALTER TABLE db2.other_tables_with_fk DROP FOREIGN KEY fk_to_TableB;
ALTER TABLE db2.other_tables_with_fk
ADD CONSTRAINT fk_to_TableB FOREIGN KEY (TableB_id)
REFERENCES TableB(id);
Then your second database will be ready to merge with the first one.
For MyISM or situations that CASCADE is not supported by engine you can simulate it manually by defining Triggers:
CREATE TRIGGER trigger1
AFTER UPDATE
ON TableB
FOR EACH ROW
BEGIN
UPDATE other_tables_with_fk1 SET TableB_id = NEW.id WHERE TableB_id = OLD.id
UPDATE other_tables_with_fk2 SET TableB_id = NEW.id WHERE TableB_id = OLD.id
...
END
Even if triggers are not available you can simply increase id number of rows in second database by some custom amount(any amount greater than max row id which used in first database) in all tables including foreign key parent table at a same time:
UPDATE TableB t SET t.id = (t.id + 10000);
UPDATE related_table_1 t SET t.TableB_id = (t.TableB_id + 10000);
UPDATE related_table_2 t SET t.TableB_id = (t.TableB_id + 10000);
...
And then you can merge those databases.

my suggestion were:
you drop the foreign key constraint of LinkA in database1
increase the foreign key of the TableA:id AND LinkA:tableA_ID (the best way were with a join) by lets say 1000 (or how much rows you have in database2)
add the constraint again (optional)
import TableA and then LinkA to database2 from database1.
If you need more help, just ask.
Best Regards
====================================
Update. Example for the update of the ids:
UPDATE
Table_A, Link_A
SET
Table_A.id = Table_A.id + 1000,
Link_A.id = Link_A.tableA_ID + 1000,
FROM
Table_A JOIN Link_A
ON
Table_A.id = Link_A.tableA_ID

If both db are identical, I believe you should name it db_B.Table_A not db_B.Table_B to avoid confusion..but for now I go along with it
--get delta id, use biggest id from db_A and db_B
--to avoid failure because of updating to existing primary key
SELECT #dbBMax := MAX(id) FROM db_B.`Table_B`;
SELECT #dbAMin := MIN(id), #dbAMax := MAX(id) FROM db_A.`Table_A`;
SET #DeltaID := IF(#dbBMax > #dbAMax, #dbBMax, #dbAMax) - #dbAMin + 1;
--drop constraint
ALTER TABLE db_A.`Link_A` DROP FOREIGN KEY `constraint_name_A`;
ALTER TABLE db_A.`Link_B` DROP FOREIGN KEY `constraint_name_B`;
--update ids
UPDATE db_A.`Table_A` SET id = id + #DeltaID;
UPDATE db_A.`Link_A` SET tableA_ID = tableA_ID + #DeltaID;
UPDATE db_A.`Link_B` SET tableA_ID = Link_A.tableA_ID + #DeltaID;
--merge tables
--assume id is auto-increment, don't use auto-increment value,
--so id manually inserted
INSERT INTO db_A.`Table_A`(`id`, `column1`, `column2`,`column3`)
SELECT `id`, `column1`, `column2`,`column3` FROM db_B.`Table_B`;
--assume id is auto-increment, use it, don't insert manually
INSERT INTO db_A.`Link_A`(`column1`, `tableA_ID`)
SELECT `column1`, `tableA_ID` FROM db_B.`Link_A`;
--assume id is auto-increment, use it, don't insert manually
INSERT INTO db_A.`Link_B`(`column1`, `tableA_ID`)
SELECT `column1`, `tableA_ID` FROM db_B.`Link_B`;
This code may add big leap on id at db_B.Table_B if db_A.Table_A have much more data that db_B.Table_B..that can be fixed easily before/after merge table..but I think its optional..

A simple way would be to update the TableB's IDs to unique range and then do the merge. If your foreign keys are properly set to cascade the change, your database will stay consistent through this operation.
You don't need to do any changes to the database schema this way, so there is no point in time when the data is not calid. You can also be sure that the IDs won't clash. Easiest way to find unique values is to take the maximum of the ID in TableA and add that to the IDs in TableB.

Related

sql query to delete all attribute except primary key

Suppose I have a database with 3 tables of rooms: room_lvl1, room_lvl2, room_lvl3 which represent 3 levels in a building. Each level has 20++ rooms. So each table of room has 20++ rows that represent the rooms in that level.
I have a need to delete all attributes in the table except the primary key which is the room number or update everything to a null value except the primary key.
Is there any query I could possibly run?
You can set values to NULL by doing:
update room_lvl1
set col1 = NULL,
col2 = NULL,
. . .;
Where col1 etc are the non-primary key columns.
That seems like a really strange thing to want to do. You could empty the tables:
truncate table room_lvl1;
That would remove all rows.
You could save, truncate, and re-insert:
create table temp_room_lvl1 as select * from room_lvl1;
truncate table room_lvl1;
insert into room_lvl1(pk)
select pk from temp_room_lvl1;

Update unique or primary keys in MySQL

I'm building a database management tool for a client to use on his own, and I'm having some problem dealing with the possibility of the update of primary/unique keys. So, given that the data for the update is passed by a PHP script on a per row basis, here's what I've come up with (from "immediatly" to "after some time"):
DELETE/INSERT instead of UPDATE (awful, I now...):
DELETE FROM table WHERE unique_key=x;
DELETE FROM table WHERE unique_key=y;
INSERT INTO table VALUES (unique_key=y, field=record1), (unique_key=x, field=record2);
Alter my primary/unique key and then substitute them with the modified value:
UPDATE table SET unique_key=x* WHERE unique_key=x;
UPDATE table SET unique_key=y* WHERE unique_key=y;
UPDATE table SET unique_key=y WHERE unique_key=x*;
UPDATE table SET unique_key=x WHERE unique_key=y*;
Add a not modifiable auto_increment field "id" to all my tables, which act as a surrogate primary key
As now, I'm on the route of adding an "id" field to everything. Other options?
Updating a primary key isn't a problem; all values in SQL (and in the relational model) are supposed to be updatable.
The problem seems to be swapping primary keys, which
doesn't make sense to me if you use surrogate keys (because they're meaningless, so updates aren't necessary) and which
doesn't make sense to me if you use natural keys, because that's like swapping my StackOverflow userid with yours.
Adding an "ID" column to every table won't help you. The "unique_key" column still has to be declared unique. Adding an "ID" column doesn't change that business requirement.
You could swap primary key values if MySQL supported deferred constraints. (Deferred constraints are a feature in standard SQL.) But MySQL doesn't support that feature. In PostgreSQL, for example, you could do this.
create table test (
unique_key char(1) primary key deferrable initially immediate,
other_column varchar(15) not null
);
insert into test values
('x', 'record2'),
('y', 'record1');
begin;
set constraints test_pkey deferred;
update test set unique_key = 'y' where other_column = 'record2';
update test set unique_key = 'x' where other_column = 'record1';
commit;
select * from test;
unique_key other_column
--
y record2
x record1
You should be able to use a CASE expression to do this kind of update. For example:
UPDATE tbl SET col =
CASE WHEN col = 1 THEN 2
WHEN col = 2 THEN 1
END
WHERE col IN (1,2);
(untested code)

How to update without altering the structure of table

I have a table which has a structure like as below.
create table test_table (id INT NOT NUll AUTO_INCREMENT
, name varchar(100),
primary key (id))ENGINE=INNODB
Select * from test_table;
id name
1 a
2 b
3 c
Now I want to increment the id by a number lets say 2
So the final results should be
Select * from test_table;
id name
3 a
4 b
5 c
The way I can do it is, first remove the PK and auto increment and then
update the table:
update test_table set id=id+2;
The other way is to make a temp table with out PK and auto increment and then
extract the result to the main table.
Is there any other way to do this without destroying the table structure ?
I am using MYSQL.
In your example, you need to remove the PK first to allow (temporary) duplicate id's during the course of the update.
To avoid duplicates, you must perform an ordered update:
UPDATE test_table SET id = id + 2 ORDER BY id DESC;
This will update records with largest value of id first, hence avoiding collision.
Obviously, if you want to decrement the values of id, then use "ORDER BY id ASC".
Here is the query to update the tables in SQL :- Its generic
UPDATE table_name SET column1=value, column2=value2,WHERE some_column=some_value;
Please follow the link for more information
Update Query
Thanks,
Pavan

How to delete rows from table sql

I want to delete specific rows from 8 tables.
My problem is that the rows are connected with foreign key.
How can I delete all the data that connected to the specific rows that I want to delete?
My tables include definition tables (like id, name ,max value, min value...),
data tables (like id, user_id, definition_id,....) and history tables (save every change in data table).
I thought to use delete on cascade command but I could not find a way to use it.
DELETE CASCADE is an attribute of the foreign key constraint. Unfortunately it's not something you can use as an option with a DELETE statement (which would be really cool actually)
If your foreign keys have not been declared as cascading you need to "work your way up".
Unfortunately you did not show us your real table structure so let's assume something like this:
main_table (main_id)
child_one (id, main_id)
child_two (id, id_one)
child_three (id, id_two)
(I know you said 8 tables, but for the sake of the demonstration I shortened it a bit, but that doesn't change the underlying "strategy")
Assuming you want to delete the row with main_id = 42 from `main_table:
You first need to delete the rows from child_three using something like this:
delete from child_three
where id_two in (select id
from child_two
where id_one in (select id
from child_one
where main_id = 42);
Then delete the rows from child_two:
delete from child_two
where id_one in (select id
from child_one
where main_id = 42);
Then child_one:
delete from child_one
where main_id = 42;
And finally the main table:
delete from main_table
where id = 42;
Some SQL clients can actually generate those statements for you. I don't know if SQL Developer can though.
I assume that you use InnoDB Engine, since you are talking about foreign keys,
Easier will be to define properly the table so that a deletion will act as a cascade deletion.
CONSTRAINT `myForeignKey` FOREIGN KEY (`typeId`)
REFERENCES `types` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
Here is alink with a proper create table statement:
How do I use on delete cascade in mysql?

how to repopulate/recreate autoincrement in mysql

I want to know if I can repopulate the autoincrement value in mysql.
Because, I have records that look similar:
ID Name
1 POP
3 OLO
12 lku
Basically , I want a way to update the ID to this
ID Name
1 POP
2 OLO
3 lku
Is there any way to do this in mysql?
Thanks.
It's not best practice to fiddle your primary keys - better to let your DB handle it itself. There can be issues if, in between the UPDATE and ALTER, another record is added. Because of this, you must LOCK the table, which might hang other queries and spike load on a busy production server.
LOCK TABLES table WRITE
UPDATE table SET id=3 WHERE id=12;
ALTER TABLE table AUTO_INCREMENT=4;
UNLOCK TABLES
OR - for thousands of rows (with no foriegn key dependencies):
CREATE TEMPORARY TABLE nameTemp( name varchar(128) not null )
INSERT INTO name SELECT name FROM firstTable
TRUNCATE firstTable
INSERT INTO firstTable SELECT name FROM nameTemp
The latter method will only work where you have no foreign keys. If you do, you'll require a lookup table.
CREATE TEMPORARY TABLE lookup( newId INTEGER AUTO_INCREMENT, oldId INTEGER, PRIMARY KEY newId( newId ) );
INSERT INTO lookup (oldId) SELECT id FROM firstTable
[do temp table queries above]
You now have a lookup table with the old to new ids which you can use to update the foreign key dependencies on your other tables (on a test server!)
Changing the primary key is a very bad idea as it endangers your referential integrity (what if another table uses the id without having a foreign key with proper "on change"?).
If you really, really have to do it and don't care about bad side-effects:
Create a second table with identical structure
INSERT INTO new_table (id, [other fields]) SELECT NULL, [other fields] FROM old_table;
DROP old_table;
RENAME new_table old_table;
Warning:
This will damage every other table that has foreign keys on this table (but if you had such then you wouldn't be doing this anyways).
You may want to try something like...
Create Temporary table MyBackup
( ID as your autoincrement,
OldID as Int for backlinking/retention,
RestOfFields as their type )
insert into MyBackup
( OldID
RestOfFields )
select
ID as OldID,
RestOfFields
from
YourOriginalTable
order by
ID (this is your original ID)
Then you'll have a new table with an autoincrement with new IDs assigned, yet have a full copy of their original ID. Then, you can do correlated updates against other tables and set the ID = ID where ID = OldID. By keeping your insert via order by the original ID, it will keep the numbers from replacing out of sequence.. Ex: if your table was orderd as
Old ID = 3, new ID = 1
Old ID = 1, new ID = 3
Old ID = 12, new ID = 2
Your old 3's will become 1's, then the 1's would become 3's, and 12's become 2's
Old ID = 1, new ID = 1
Old ID = 3, new ID = 2
Old ID = 12, new ID = 3
your 3's won't overwrite the higher number, and the 12's won't conflict with the 3's since the threes were already lowered to 2's.