93

If I have a large number of SQLite databases, all with the same schema, what is the best way to merge them together in order to perform a query on all databases?

I know it is possible to use ATTACH to do this but it has a limit of 32 and 64 databases depending on the memory system on the machine.

davidmytton
  • 38,604
  • 37
  • 87
  • 93

7 Answers7

108

To summarize from the Nabble post in DavidM's answer:

attach 'c:\test\b.db3' as toMerge;           
BEGIN; 
insert into AuditRecords select * from toMerge.AuditRecords; 
COMMIT; 
detach toMerge;

Repeat as needed.

Note: added detach toMerge; as per mike's comment.

xanth
  • 172
  • 2
  • 12
dfrankow
  • 20,191
  • 41
  • 152
  • 214
  • 9
    and also `detach toMerge;` *after* committing. – mike Apr 13 '15 at 11:30
  • 4
    This would only merge one table I suppose. How would you import multiple tables at once, preserving their foreignkey ? – ILoveCoding May 29 '15 at 14:50
  • I tried this, but the columns from the attached db where misaligned for some reason and the the result was wrong. Therefore, I had to explicitely state column names in the same order in the INSERT and SELECT statements. – Pimin Konstantin Kefaloukos Aug 08 '16 at 02:36
  • 3
    How to cope with UNIQUE constraint and update rows which have same specific columns? – Mithril Sep 14 '16 at 01:55
  • how do you do this in a script for a dynamic number of databases? – user5359531 May 25 '18 at 21:23
  • 1
    @user5359531 I would write something in some other language (like python) to emit the SQL commands to do this as above, get the SQL, run the SQL. – dfrankow May 25 '18 at 23:22
  • @Mithril To avoid an "Error: UNIQUE constraint failed: xxx.id", replace the insert statement with "insert into AuditRecords (column1, column2, .. columnN) select column1, column2, .. columnN from toMerge.AuditRecords; " – user553965 Oct 19 '20 at 13:58
  • To accomplish this with more than one table all-at-once, while also eliminating the cause of UNIQUE constraint problems (i.e. duplicates) - use the UNION operation: https://www.sqlitetutorial.net/sqlite-union/ (e.g. accach 'a.db' as a; attach 'b.db' as b; BEGIN; insert into table select * from a.table UNION select * from b.table; COMMIT; –  May 29 '21 at 16:11
  • These apparently make it 100x faster, by the way (at the risk of data corruption if anything interrupts it, and probably cannot read from the target DB at the same time): PRAGMA journal_mode=WAL; PRAGMA synchronous = OFF; –  Mar 12 '22 at 11:26
  • I had to remove the BEGIN; Just did the seperate steps and it worked. Thanks a lot! – tm1701 Mar 10 '23 at 15:39
11

Although a very old thread, this is still a relevant question in today's programming needs. I am posting this here because none of the answers provided yet is concise, easy, and straight-to-point. This is for sake of Googlers that end up on this page. GUI we go:

  1. Download Sqlitestudio
  2. Add all your database files by using the Ctrl + O keyboard shortcut
  3. Double-click each now-loaded db file to open/activate/expand them all
  4. Fun part: simply right-click on each of the tables and click on Copy, and then go to the target database in the list of the loaded database files (or create new one if required) and right-click on the target db and click on Paste

I was wowed to realize that such a daunting task can be solved using the ancient programming skill called: copy-and-paste :)

Damilola Olowookere
  • 2,253
  • 2
  • 23
  • 33
11

Here is a simple python code to either merge two database files or scan a directory to find all database files and merge them all together (by simply inserting all data in other files to the first database file found).Note that this code just attaches the databases with the same schema.

import sqlite3
import os


def merge_databases(db1, db2):
    con3 = sqlite3.connect(db1)

    con3.execute("ATTACH '" + db2 +  "' as dba")

    con3.execute("BEGIN")
    for row in con3.execute("SELECT * FROM dba.sqlite_master WHERE type='table'"):
        combine = "INSERT OR IGNORE INTO "+ row[1] + " SELECT * FROM dba." + row[1]
        print(combine)
        con3.execute(combine)
    con3.commit()
    con3.execute("detach database dba")


def read_files(directory):
    fname = []
    for root,d_names,f_names in os.walk(directory):
        for f in f_names:
            c_name = os.path.join(root, f)
            filename, file_extension = os.path.splitext(c_name)
            if (file_extension == '.sqlitedb'):
                fname.append(c_name)

    return fname

def batch_merge(directory):
    db_files = read_files(directory)
    for db_file in db_files[1:]:
        merge_databases(db_files[0], db_file)

if __name__ == '__main__':
    batch_merge('/directory/to/database/files')
Mohammadsadegh
  • 111
  • 1
  • 6
  • 3
    for db_file in db_files[1:]: – Simon Allfrey May 06 '22 at 18:24
  • Just adapt the extension if your file didnt have the same as the code, and it works perfectly. – Carlos Garcia Mar 28 '23 at 18:42
  • When I tried to merge two db files which contain different tables, I got the error sqlite3.IntegrityError: UNIQUE constraint failed: names.id. It turned out that if a table doesn't exist in the db1, the insert statement was trying to insert data into the db2 instead of db1. So I had to add some codes to create the table first in db1. – Shiping May 24 '23 at 13:18
  • This made me run into an `sqlite3.OperationalError: near "-": syntax error` error when the `merge_databases` function tried to merge the history tables. I had to change the following line: `for row in con3.execute("SELECT * FROM dba.sqlite_master WHERE type='table' AND name NOT LIKE 'History_%'" ):` – emilaz Jun 08 '23 at 13:55
2

Late answer, but you can use:

#!/usr/bin/python

import sys, sqlite3

class sqlMerge(object):
    """Basic python script to merge data of 2 !!!IDENTICAL!!!! SQL tables"""

    def __init__(self, parent=None):
        super(sqlMerge, self).__init__()

        self.db_a = None
        self.db_b = None

    def loadTables(self, file_a, file_b):
        self.db_a = sqlite3.connect(file_a)
        self.db_b = sqlite3.connect(file_b)

        cursor_a = self.db_a.cursor()
        cursor_a.execute("SELECT name FROM sqlite_master WHERE type='table';")

        table_counter = 0
        print("SQL Tables available: \n===================================================\n")
        for table_item in cursor_a.fetchall():
            current_table = table_item[0]
            table_counter += 1
            print("-> " + current_table)
        print("\n===================================================\n")

        if table_counter == 1:
            table_to_merge = current_table
        else:
            table_to_merge = input("Table to Merge: ")

        return table_to_merge

    def merge(self, table_name):
        cursor_a = self.db_a.cursor()
        cursor_b = self.db_b.cursor()

        new_table_name = table_name + "_new"

        try:
            cursor_a.execute("CREATE TABLE IF NOT EXISTS " + new_table_name + " AS SELECT * FROM " + table_name)
            for row in cursor_b.execute("SELECT * FROM " + table_name):
                print(row)
                cursor_a.execute("INSERT INTO " + new_table_name + " VALUES" + str(row) +";")

            cursor_a.execute("DROP TABLE IF EXISTS " + table_name);
            cursor_a.execute("ALTER TABLE " + new_table_name + " RENAME TO " + table_name);
            self.db_a.commit()

            print("\n\nMerge Successful!\n")

        except sqlite3.OperationalError:
            print("ERROR!: Merge Failed")
            cursor_a.execute("DROP TABLE IF EXISTS " + new_table_name);

        finally:
            self.db_a.close()
            self.db_b.close()

        return

    def main(self):
        print("Please enter name of db file")
        file_name_a = input("File Name A:")
        file_name_b = input("File Name B:")

        table_name = self.loadTables(file_name_a, file_name_b)
        self.merge(table_name)

        return

if __name__ == '__main__':
    app = sqlMerge()
    app.main()

SRC : Tool to merge identical SQLite3 databases

Pedro Lobito
  • 94,083
  • 31
  • 258
  • 268
  • Does this merges the two databases or drop all from slave and insert all from master database ? If it does that, a simple file copy does the same but faster. The point is to merge only the differences... – firephil May 07 '21 at 03:55
1

If you only need to do this merge operation once (to create a new bigger database), you could create a script/program that will loop all your sqlite databases and then insert the data into your main (big) database.

Espo
  • 41,399
  • 21
  • 132
  • 159
-2

If you have reached the bottom of this feed and yet didn't find your solution, here is also a way to merge the tables of 2 or more sqlite databases.

First try to download and install DB browser for sqlite database. Then try to open your databases in 2 windows and try merging them by simply drag and drop tables from one to another. But the problem is that you can just drag and drop only one table at a time and therefore its not really a solution for this answer specifically but yet it can used to save some time from further searches if your database is small.

Taba
  • 3,850
  • 4
  • 36
  • 51
-14

With no offense, just as one developer to another, I'm afraid that your idea seems terribly inefficient. It seems to me that instead of uniting SQLite databases you should probably be storing several tables within the same Database file.

However if I'm mistaken I guess you could ATTACH the databases and then use a VIEW to simplify your queries. Or make an in-memory table and copy over all the data (but that's even worse performance wise, especially if you have large databases)

Robert Gould
  • 68,773
  • 61
  • 187
  • 272
  • 2
    Merging two databases is useful when a database engineer is working with a different copy, in order to modify the structure or content in certain ways, and in the meantime the older, live version is being modified by users. – Beejor Sep 12 '15 at 20:59
  • I appreciate such feedback, but this would be better suited to a comment on the original question, rather than an "answer". – Elle Fie Jun 28 '20 at 22:59