如何将房间数据库导出为 .CSV

Far*_*vić 3 database csv android kotlin android-room

如何将我的房间数据库导出到 .CSV 文件。我希望将其保存到设备存储中。我搜索了所有内容,但没有合适的答案。我希望有办法做到这一点。

Mik*_*keT 6

您不能仅将数据库另存为 CSV。然而,如果完全设置检查点,数据库只是一个文件。如果没有完全检查点,那么它(除非预写日志记录被禁用)将是三个文件。

数据库本身由各个部分组成,一个标头(文件的前 100 个字节),然后是各个组件的数据块。其中大部分依赖于架构(表),还有系统表

  • sqlite_master 是一个保存模式的表
  • 如果 autogenerate = true 用于整数类型主键,则还有 sqlite_sequence 表
  • room 本身有 room_master_table,其中 room 存储哈希值,该哈希值与基于 Room 预期模式的编​​译哈希值进行比较。

要将所有数据保存为 CSV,将会很复杂(而且没有必要,因为您只需复制数据库文件即可)。

如果您想要的是应用程序数据的 CSV,那么这将取决于表格。如果您只有一个表,那么将数据提取为 CSV 会相对简单,但如果数据包含逗号,则可能会很复杂。

如果有多个表,那么就必须区分表的数据。

同样,如果只是保护数据,最简单的方法就是复制文件。

然而,作为基于以下的示例:-

一个数据库有 3 个表(除了系统表)

  • PostDataLocal(列见下文)
  • 组数据本地
  • 管理数据本地
    • 现有答案已针对该示例进行了调整

然后:-

@Dao 注解接口(即 AllDao)中的以下内容:-

@Query("SELECT postId||','||content FROM postDataLocal")
fun getPostDataLocalCSV(): List<String>
@Query("SELECT groupPostIdMap||','||groupId||','||groupName FROM groupDataLocal")
fun getGroupDataLocalCSV(): List<String>
@Query("SELECT adminGroupIdMap||','||userId||','||adminName||','||avatar FROM adminDataLocal")
fun getAdminDataLocalCSV(): List<String>
Run Code Online (Sandbox Code Playgroud)

以下函数dao是先前实例化的 AllDao 实例:-

private fun createCSV() {

    val sb = StringBuilder()
    var afterFirst = false
    sb.append("{POSTDATALOCAL}")
    for (s in dao.getPostDataLocalCSV()) {
        if(afterFirst) sb.append(",")
        afterFirst = true
        sb.append(s)
    }
    afterFirst = false
    sb.append("{GROUPDATALOCAL}")
    for (s in dao.getGroupDataLocalCSV()) {
        if (afterFirst) sb.append(",")
        afterFirst = true
        sb.append(s)
    }
    afterFirst = false
    sb.append("{ADMINDATALOCAL}")
    for (s in dao.getAdminDataLocalCSV()) {
        if ((afterFirst)) sb.append(",")
        afterFirst = true
        sb.append(s)
    }
    Log.d("CSV_DATA","CSV is :-\n\t$sb")

}
Run Code Online (Sandbox Code Playgroud)

然后在一个活动(dao 已被实例化)中执行以下操作:-

createCSV() 
Run Code Online (Sandbox Code Playgroud)

然后,当数据库包含以下数据(通过应用程序检查提取)时:-

发布数据本地

  • 在此输入图像描述

组数据本地

  • 在此输入图像描述

管理数据本地

  • 在此输入图像描述

写入日志的结果(可以写入文件而不是日志)是:-

D/CSV_DATA: CSV is :-
        {POSTDATALOCAL}1,Post001,2,Post002,3,Post003{GROUPDATALOCAL}1,1,Group001 (Post001),1,2,Group002 (Post001),1,3,Group003 (Post001),2,4,Group004 (Post002),2,5,Group005 (Post002),3,6,Group006 (Post003){ADMINDATALOCAL}1,1,Admin001,admin001.gif,1,2,Admin002,admin002.gif,1,3,Admin003,admin003.gif,2,4,Admin004,admin004.gif,2,5,Admin005,admin005.gif,3,6,Admin006,admin006.gif,4,7,Admin007,admin007.gif,5,8,Admin008,admin008.gif,6,9,Admin009,admin009.gif,6,10,Admin010,admin010.gif
Run Code Online (Sandbox Code Playgroud)
  • 请注意如何包含标题来区分表
  • 当然,没有考虑在数据中包含逗号(上面的内容只是为了表明原则上您可以相对轻松地生成数据的 CSV 表示形式)

额外的

这是一个更加自动化的版本,您不需要创建 @Query 带注释的函数,而是询问 sqlite_master 来提取表,并使用 table_info pragma 来确定列,构建相应的 SQL。

因此它应该适合任何 Room 数据库。

它还允许用逗号指示符替换数据中的逗号,然后在处理 CSV 时可以替换该逗号指示符。

支持(次要/由主要调用)功能是:-

private fun getTableColumnNames(tableName: String, suppDB: SupportSQLiteDatabase): List<String> {
    val rv = arrayListOf<String>()
    val csr = suppDB.query("SELECT name FROM pragma_table_info('${tableName}')",null)
    while (csr.moveToNext()) {
        rv.add(csr.getString(0))
    }
    csr.close()
    return rv.toList()
}
Run Code Online (Sandbox Code Playgroud)

主要功能:-

private fun AutoCreateCSV(): String {
    val replaceCommaInData = "{COMMA}" /* commas in the data will be replaced by this */
    val rv = StringBuilder()
    val sql = StringBuilder()
    var afterFirstTable = false
    var afterFirstColumn = false
    var afterFirstRow = false
    val suppDb = db.getOpenHelper().writableDatabase
    var currentTableName: String = ""
    val csr = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE('sqlite_%') AND name NOT LIKE('room_%') AND name NOT LIKE('android_%')", null)
    while (csr.moveToNext()) {
        sql.clear()
        sql.append("SELECT ")
        currentTableName = csr.getString(0)
        if (afterFirstTable) rv.append(",")
        afterFirstTable = true
        afterFirstColumn = false
        rv.append("{$currentTableName},")
        for (columnName in getTableColumnNames(currentTableName,suppDb)) {
            if (afterFirstColumn) sql.append("||','||")
            afterFirstColumn = true
            sql.append("replace(`$columnName`,',','$replaceCommaInData')")
        }
        sql.append(" FROM `${currentTableName}`")
        val csr2 = db.query(sql.toString(),null)
        afterFirstRow = false
        while (csr2.moveToNext()) {
            if (afterFirstRow) rv.append(",")
            afterFirstRow = true
            rv.append(csr2.getString(0))
        }
        csr2.close()
    }
    csr.close()
    return rv.toString()
}
Run Code Online (Sandbox Code Playgroud)

使用相同的数据并作为主函数返回一个字符串,以下代码Log.d("CSV_DATA2",AutoCreateCSV())会导致:-

D/CSV_DATA2: {PostDataLocal},1,Post001,2,Post002,3,Post003,{GroupDataLocal},1,1,Group001 (Post001),1,2,Group002 (Post001),1,3,Group003 (Post001),2,4,Group004 (Post002),2,5,Group005 (Post002),3,6,Group006 (Post003),{AdminDataLocal},1,1,Admin001,admin001.gif,1,2,Admin002,admin002.gif,1,3,Admin003,admin003.gif,2,4,Admin004,admin004.gif,2,5,Admin005,admin005.gif,3,6,Admin006,admin006.gif,4,7,Admin007,admin007.gif,5,8,Admin008,admin008.gif,6,9,Admin009,admin009.gif,6,10,Admin010,admin010.gif 
Run Code Online (Sandbox Code Playgroud)

如果数据包含逗号,例如 Post001 将更改为值Post001, <<note the comma in the data>>

然后 :-

D/CSV_DATA2: {PostDataLocal},1,Post001{COMMA} <<note the comma in the data>>,2,Post002,3 ....
Run Code Online (Sandbox Code Playgroud)
  • 这个附加解决方案还修复了第一个解决方案中的一个小错误,即标题和数据之间省略了一些分隔逗号。