简体   繁体   中英

How to merge two sqlite databases without losing foreign keys?

I'm developing mobile app for learning a foreign language using flutter and sqflite package. The app has sqlite database with tables for words and some kind of collections (relation many to many). This data is stored in the app asset and is loads to mobile database directory when the app is installing like in sqflite guide https://github.com/tekartik/sqflite/blob/master/sqflite/doc/opening_asset_db.md
User can add own words and collections as well as I wanna be able to populate or change mine. I used to think that I can achieve that with sqlite upsert but in case that user added the same word as I did in app's update, there is appears a conflict. If I do upsert then this word doesn't appear in my word collections because they are bound by id, otherwise, if I do replace , then word disappear from user's collections.

I think I need some kind of cascade id update in sqlite insert or replace , but there is no such feature.

Any ideas, please?

I think I need some kind of cascade id update in sqlite insert or replace, but there is no such feature.

There is such a feature; you specify ON UPDATE CASCADE as part of the FOREIGN KEY definition/clause. You can also code ON DELETE CASCADE SQLite Foreign Key Support - 4.3. ON DELETE and ON UPDATE Actions .

As this is coded as part of the child table and that you cannot use ALTER TABLE COLUMN to change the column's attributes (other than to RENAME a column). You will have to :-

  1. Rename the child table.
  2. Create the replacement table using the original name with the change and then use INSERT INTO replacement_table_with_original_name SELECT * FROM renamed_table (obviously changing replacement_table_with_original_name and renamed_table ) to the actual names.
  3. When happy DROP the renamed table.

Example

For example consider the following representation of your scenario (from the available information) then this will add the ON UPDATE CASCADE and ON DELETE CASCADE whilst keeping the existing data, it additionally deletes a row without conflict and also updates a referenced value without conflict or loss :-

-- Create the original tables and load some test data
CREATE TABLE IF NOT EXISTS word_table (id INTEGER PRIMARY KEY, other_column TEXT);
INSERT INTO word_table VALUES (null,'WA'),(null,'WB'),(null,'WC');
CREATE TABLE IF NOT EXISTS collection_table (id INTEGER PRIMARY KEY, other_column TEXT);
INSERT INTO collection_table VALUES (null,'CA'),(null,'CB'),(null,'CC');
CREATE TABLE IF NOT EXISTS word_collection_map (
    word_reference INTEGER NOT NULL REFERENCES word_table(id), 
    collection_reference  INTEGER NOT NULL REFERENCES collection_table(id), 
    PRIMARY KEY (word_reference,collection_reference) ) WITHOUT ROWID;
INSERT INTO word_collection_map VALUES(1,1),(3,2),(3,3),(2,1),(2,2),(2,3);

-- show the result
SELECT word_table.other_column, collection_table.other_column 
FROM word_collection_map 
    JOIN word_table ON word_table.id = word_reference
    JOIN collection_table ON collection_table.id = collection_reference
;

-- ADDING CASCADE
ALTER TABLE word_collection_map RENAME TO old_word_collection_map;
CREATE TABLE IF NOT EXISTS word_collection_map (
    word_reference INTEGER NOT NULL REFERENCES word_table(id) ON UPDATE CASCADE ON DELETE CASCADE , 
    collection_reference  INTEGER NOT NULL REFERENCES collection_table(id)  ON UPDATE CASCADE ON DELETE CASCADE, 
    PRIMARY KEY (word_reference,collection_reference) ) WITHOUT ROWID;
INSERT INTO word_collection_map SELECT * FROM old_word_collection_map;
DROP TABLE IF EXISTS old_word_collection_map;
-- Show results (should match first)
SELECT word_table.other_column, collection_table.other_column 
FROM word_collection_map 
    JOIN word_table ON word_table.id = word_reference
    JOIN collection_table ON collection_table.id = collection_reference
;

-- DELETE a word no conflicts
DELETE FROM word_table WHERE other_column = 'WC';
SELECT word_table.other_column, collection_table.other_column 
FROM word_collection_map 
    JOIN word_table ON word_table.id = word_reference
    JOIN collection_table ON collection_table.id = collection_reference
;

-- UPDATE the FK parent no conflicts
UPDATE collection_table SET id = 10 WHERE id = 3;
SELECT word_table.other_column, collection_table.other_column 
FROM word_collection_map 
    JOIN word_table ON word_table.id = word_reference
    JOIN collection_table ON collection_table.id = collection_reference
;

Results :-

The log :-

-- Create the original tables and load some test data
CREATE TABLE IF NOT EXISTS word_table (id INTEGER PRIMARY KEY, other_column TEXT)
> OK
> Time: 0.118s


INSERT INTO word_table VALUES (null,'WA'),(null,'WB'),(null,'WC')
> Affected rows: 3
> Time: 0.104s


CREATE TABLE IF NOT EXISTS collection_table (id INTEGER PRIMARY KEY, other_column TEXT)
> OK
> Time: 0.097s


INSERT INTO collection_table VALUES (null,'CA'),(null,'CB'),(null,'CC')
> Affected rows: 3
> Time: 0.094s


CREATE TABLE IF NOT EXISTS word_collection_map (
    word_reference INTEGER NOT NULL REFERENCES word_table(id), 
    collection_reference  INTEGER NOT NULL REFERENCES collection_table(id), 
    PRIMARY KEY (word_reference,collection_reference) ) WITHOUT ROWID
> OK
> Time: 0.097s


INSERT INTO word_collection_map VALUES(1,1),(3,2),(3,3),(2,1),(2,2),(2,3)
> Affected rows: 6
> Time: 0.105s


-- show the result
SELECT word_table.other_column, collection_table.other_column 
FROM word_collection_map 
    JOIN word_table ON word_table.id = word_reference
    JOIN collection_table ON collection_table.id = collection_reference
> OK
> Time: 0.001s


-- ADDING CASCADE
ALTER TABLE word_collection_map RENAME TO old_word_collection_map
> OK
> Time: 0.108s


CREATE TABLE IF NOT EXISTS word_collection_map (
    word_reference INTEGER NOT NULL REFERENCES word_table(id) ON UPDATE CASCADE ON DELETE CASCADE , 
    collection_reference  INTEGER NOT NULL REFERENCES collection_table(id)  ON UPDATE CASCADE ON DELETE CASCADE, 
    PRIMARY KEY (word_reference,collection_reference) ) WITHOUT ROWID
> OK
> Time: 0.096s


INSERT INTO word_collection_map SELECT * FROM old_word_collection_map
> Affected rows: 6
> Time: 0.094s


DROP TABLE IF EXISTS old_word_collection_map
> OK
> Time: 0.081s


-- Show results (should match first)
SELECT word_table.other_column, collection_table.other_column 
FROM word_collection_map 
    JOIN word_table ON word_table.id = word_reference
    JOIN collection_table ON collection_table.id = collection_reference
> OK
> Time: 0s


-- DELETE a word no conflicts
DELETE FROM word_table WHERE other_column = 'WC'
> Affected rows: 1
> Time: 0.086s


SELECT word_table.other_column, collection_table.other_column 
FROM word_collection_map 
    JOIN word_table ON word_table.id = word_reference
    JOIN collection_table ON collection_table.id = collection_reference
> OK
> Time: 0.001s


-- UPDATE the FK parent no conflicts
UPDATE collection_table SET id = 10 WHERE id = 3
> Affected rows: 1
> Time: 0.089s


SELECT word_table.other_column, collection_table.other_column 
FROM word_collection_map 
    JOIN word_table ON word_table.id = word_reference
    JOIN collection_table ON collection_table.id = collection_reference
> OK
> Time: 0s

Result 1 (base data)

在此处输入图片说明

Result 2 (after schema change)

在此处输入图片说明

Result 3 (after delete)

在此处输入图片说明

Result 4 (after update)

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM