Attach two SQLite-DB files to query over both files in Swift

拟墨画扇 提交于 2021-02-10 15:53:41

问题


In Swift4 for iOS11 I would like to attach two SQLite database-files (file1.db and file2.db) in order to make a query over all data (i.e. over both databases).

I have successfully implemented a SQL-query in one SQLite database using the following code (..here with an example of a name-query...):

func readOneDB() -> String? {

    // create SQL-DB FilePath
    do {
        // create a documents-URL from filename
        self.file_1_URL = try createSQLFilePath(fileName: "file1", fileExtension: "db")
    }
    catch {
        return nil
    }
    let query =
    """
    SELECT DISTINCT n.locations
    FROM names n
    WHERE  n.name = "\(self.myName)"
    """

    // Open SQLite database
    var db: OpaquePointer? = nil
    if sqlite3_open(self.file_1_URL?.absoluteString, &db) == SQLITE_OK {

        var statement: OpaquePointer? = nil

        // Run SELECT query from db
        if sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK {

            // Loop through all results from query
            while sqlite3_step(statement) == SQLITE_ROW {

                let name = sqlite3_column_text(statement, 0)
                if name != nil {
                    let versionString = String(cString:name!)
                    return nameString
                } else {
                    print("name not found", terminator: "")
                    return nil
                }
            }
        } else {
            let errmsg = String(cString: sqlite3_errmsg(db))
            print("error running query: \(errmsg)")
            return nil
        }
    } else {
        print("error opening database")
        return nil
    }
    return nil
}

Now, I read that with the following sql-cmd it is possible to attach two SQL-databases together (in order to make a query over both of them): ATTACH

In fact, I have the following code that does not throw an exception - but still not working 100%

let attachStr = "ATTACH DATABASE \'\(self.file_2_URL!)\' AS SECOND"
if (sqlite3_exec(db, attachStr, nil, &statement, nil) == SQLITE_OK) {
    print("Success!")
}

There is a print of "Success!" when running the code.

Of course, getting the success-message with the attach-code, I thought this is it (and the db-object has both databases united)

But actually NO ! : The query still only brings results from the first database (i.e. file1) and no query-results of the attached second file2, unfortunately.

What is wrong ?

Here is the complete code with the attachment-code and the query (see below).

Why is the query still only delivering results from the first database (file1) and not from both databases (i.e. file1 and file2) ????

What changes do I need to make in the code for this to work ????

func readTwoDBs() -> String? {

    // create SQL-DB FilePath
    do {
        // create documents-URLs for two filenames
        self.file_1_URL = try createSQLFilePath(fileName: "file1", fileExtension: "db")
        self.file_2_URL = try createSQLFilePath(fileName: "file2", fileExtension: "db")
    }
    catch {
        return nil
    }
    let query =
    """
    SELECT DISTINCT n.locations
    FROM names n
    WHERE  n.name = "\(self.myName)"
    """

    // Open SQLite database
    var db: OpaquePointer? = nil
    if sqlite3_open(self.file_1_URL?.absoluteString, &db) == SQLITE_OK {

        var statement: OpaquePointer? = nil

        let attachStr = "ATTACH DATABASE \'\(self.file_2_URL!)\' AS SECOND"
        if (sqlite3_exec(db, attachStr, nil, &statement, nil) == SQLITE_OK) {
            print("Success!")
        }

        //// !!!!!!! Success is printed but the query still only works with file1 WHY ????????????????????

        // Run SELECT query from db
        statement = nil
        if sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK {

            // Loop through all results from query
            while sqlite3_step(statement) == SQLITE_ROW {

                let name = sqlite3_column_text(statement, 0)
                if name != nil {
                    let versionString = String(cString:name!)
                    return nameString
                } else {
                    print("name not found", terminator: "")
                    return nil
                }
            }
        } else {
            let errmsg = String(cString: sqlite3_errmsg(db))
            print("error running query: \(errmsg)")
            return nil
        }
    } else {
        print("error opening database")
        return nil
    }
    return nil
}

回答1:


With the help of Joakim, I think I found a solution:

The ATTACH-cmd actually means that more than one database is sharing the same database-connection. As Joakim pointed out, one still needs to do two queries if one wants to get results from both database-files!

Here is the code : (for illustration purposes, the method's return-value is a tuple containing the two results of the two queries (one from the first database, second one from the second database)

func readTwoDBs() -> (String, String)? {

    var returnValue_1: String = ""
    var returnValue_2: String = ""

    // create SQL-DB FilePath
    do {
        // create documents-URLs for two filenames
        self.file_1_URL = try createSQLFilePath(fileName: "file1", fileExtension: "db")
        self.file_2_URL = try createSQLFilePath(fileName: "file2", fileExtension: "db")
    }
    catch {
        return nil
    }
    let query_1 =
    """
    SELECT DISTINCT n.locations
    FROM names n
    WHERE  n.name = "\(self.myName)"
    """

    // !! FOR QUERY_2 YOU NEED THE "MY_DB_TWO" NAME AND ITS TABLE IN THE FROM STATEMENT !!!!!!!
    // !! THIS "MY_DB_TWO" IS GIVEN BY THE ATTACH-cmd FURTHER DOWN... !!!!!!!! 
    let query_2 =
    """
    SELECT DISTINCT n.locations
    FROM MY_DB_TWO.names n
    WHERE  n.name = "\(self.myName)"
    """

    // Open SQLite database
    var db: OpaquePointer? = nil
    if sqlite3_open(self.file_1_URL?.absoluteString, &db) == SQLITE_OK {

        var statement: OpaquePointer? = nil

        let attachStr = "ATTACH DATABASE \'\(self.file_2_URL!)\' AS MY_DB_TWO"
        if (sqlite3_exec(db, attachStr, nil, &statement, nil) == SQLITE_OK) {
            print("Success!")
        }

        // Run SELECT query Nr1 from main.db
        statement = nil
        if sqlite3_prepare_v2(db, query_1, -1, &statement, nil) == SQLITE_OK {

            // Loop through all results from query
            while sqlite3_step(statement) == SQLITE_ROW {

                let name = sqlite3_column_text(statement, 0)
                if name != nil {
                    let nameString = String(cString:name!)
                    returnValue_1 = nameString
                } else {
                    print("name not found", terminator: "")
                    returnValue_1 = ""
                }
            }
        } else {
            let errmsg = String(cString: sqlite3_errmsg(db))
            print("error running query: \(errmsg)")
            returnValue_1 = ""
        }

        // Run SELECT query Nr2 from MY_DB_TWO
        statement = nil
        if sqlite3_prepare_v2(db, query_2, -1, &statement, nil) == SQLITE_OK {

            // Loop through all results from query
            while sqlite3_step(statement) == SQLITE_ROW {

                let name = sqlite3_column_text(statement, 0)
                if name != nil {
                    let nameString = String(cString:name!)
                    returnValue_2 = nameString
                } else {
                    print("name not found", terminator: "")
                    returnValue_2 = ""
                }
            }
        } else {
            let errmsg = String(cString: sqlite3_errmsg(db))
            print("error running query: \(errmsg)")
            returnValue_2 = ""
        }
    } else {
        print("error opening database")
    }

    return (returnValue_1, returnValue_2)
}


来源:https://stackoverflow.com/questions/49440681/attach-two-sqlite-db-files-to-query-over-both-files-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!