从 SQLite 数据库中读取许多表并在 R 中组合

reading in many tables from SQLlite databases and combining in R

我正在使用一个输出结果数据库的程序。我有数百个结构相同的数据库,我想将它们组合成一个大数据库。我最感兴趣的是每个数据库中的 1 table。我不太使用 databases/sql,但它会简化流程中的其他步骤,跳过输出 csv。

之前,我通过导出 csv 并使用这些步骤合并所有 csv 来完成此操作:

所有 CSV 的向量并组合

library(DBI)
library(RSQLite)
library(dplyr)

csv_locs<- list.files(newdir, recursive = TRUE, pattern="*.csv", full.names = TRUE)

pic_dat <- do.call("rbind", lapply(csv_locs, 
FUN=function(files){data.table::fread(files, data.table = FALSE)}))

如何使用 sql 类型的数据库 tables 来做到这一点?

我基本上是拉出第一个 table,然后通过循环加入其余部分。

db_locs <- list.files(directory, recursive = TRUE, pattern="*.ddb", full.names = TRUE)


# first table
con1<- DBI::dbConnect(RSQLite::SQLite(), db_locs [1])
start <- tbl(con1, "DataTable")

# open connection to location[i], get table, union, disconnect; repeat. 
for(i in 2:length(db_locs )){
con <- DBI::dbConnect(RSQLite::SQLite(), db_locs[i])
y <- tbl(con, "DataTable")
start <- union(start, y, copy=TRUE)
dbDisconnect(con)
}

这太慢了!好吧,公平地说,它的大数据和 csv 也很慢。

老实说,我想我写了最慢的方法来做到这一点 :) 我做不到。call/lapply 在这里工作的选项,但也许我遗漏了一些东西。

这看起来类似于 "iterative rbinding of frames",因为每次您这样做 union,它都会将整个 table 复制到一个新对象(未经证实,但这是我的直觉)。这可能适用于少数人,但扩展性很差。我建议您将所有 table 收集到一个列表中,并在最后调用 data.table::rbindlist 一次 ,然后插入 table.

没有你的数据,我想办法。因为我不能完全确定每个 sqlite3 文件是否只有一个 table,所以我将为每个数据库添加两个 table。如果你只有一个,解决方案很容易简化。

for (i in 1:3) {
  con <- DBI::dbConnect(RSQLite::SQLite(), sprintf("mtcars_%d.sqlite3", i))
  DBI::dbWriteTable(con, "mt1", mtcars[1:3,1:3])
  DBI::dbWriteTable(con, "mt2", mtcars[4:5,4:7])
  DBI::dbDisconnect(con)
}
(lof <- list.files(pattern = "*.sqlite3", full.names = TRUE))
# [1] "./mtcars_1.sqlite3" "./mtcars_2.sqlite3" "./mtcars_3.sqlite3"

现在我将遍历它们并读取 table

的内容
allframes <- lapply(lof, function(fn) {
  con <- DBI::dbConnect(RSQLite::SQLite(), fn)
  mt1 <- tryCatch(DBI::dbReadTable(con, "mt1"),
                  error = function(e) NULL)
  mt2 <- tryCatch(DBI::dbReadTable(con, "mt2"),
                  error = function(e) NULL)
  DBI::dbDisconnect(con)
  list(mt1 = mt1, mt2 = mt2)
})
allframes
# [[1]]
# [[1]]$mt1
#    mpg cyl disp
# 1 21.0   6  160
# 2 21.0   6  160
# 3 22.8   4  108
# [[1]]$mt2
#    hp drat    wt  qsec
# 1 110 3.08 3.215 19.44
# 2 175 3.15 3.440 17.02
# [[2]]
# [[2]]$mt1
#    mpg cyl disp
# 1 21.0   6  160
# 2 21.0   6  160
# 3 22.8   4  108
### ... repeated

从这里开始,只需将它们组合在 R 中并写入新数据库。虽然您可以使用 do.call(rbind,...)dplyr::bind_rows,但您已经提到了 data.table,所以我会坚持使用:

con <- DBI::dbConnect(RSQLite::SQLite(), "mtcars_all.sqlite3")
DBI::dbWriteTable(con, "mt1", data.table::rbindlist(lapply(allframes, `[[`, 1)))
DBI::dbWriteTable(con, "mt2", data.table::rbindlist(lapply(allframes, `[[`, 2)))
DBI::dbGetQuery(con, "select count(*) as n from mt1")
#   n
# 1 9
DBI::dbDisconnect(con)

如果您无法一次将它们全部加载到 R 中,则将它们实时附加到 table:

con <- DBI::dbConnect(RSQLite::SQLite(), "mtcars_all2.sqlite3")
for (fn in lof) {
  con2 <- DBI::dbConnect(RSQLite::SQLite(), fn)
  mt1 <- tryCatch(DBI::dbReadTable(con2, "mt1"), error = function(e) NULL)
  if (!is.null(mt1)) DBI::dbWriteTable(con, "mt1", mt1, append = TRUE)
  mt2 <- tryCatch(DBI::dbReadTable(con2, "mt2"), error = function(e) NULL)
  if (!is.null(mt1)) DBI::dbWriteTable(con, "mt2", mt2, append = TRUE)
  DBI::dbDisconnect(con2)
}
DBI::dbGetQuery(con, "select count(*) as n from mt1")
#   n
# 1 9

这不会遭受您正在经历的迭代减速。

考虑 ATTACH 为您从中导入的数据库创建模式:

library(DBI)

file1 <- tempfile(fileext = ".sqlite")
file2 <- tempfile(fileext = ".sqlite")

con1 <- dbConnect(RSQLite::SQLite(), dbname = file1)
con2 <- dbConnect(RSQLite::SQLite(), dbname = file2)

dbWriteTable(con1, "iris", iris[1:3, ])
dbWriteTable(con2, "iris", iris[4:6, ])

# Main connection
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbExecute(con, paste0("ATTACH '", file1, "' AS con1"))
#> [1] 0
dbExecute(con, paste0("ATTACH '", file2, "' AS con2"))
#> [1] 0

dbGetQuery(con, "SELECT * FROM con1.iris UNION ALL SELECT * FROM con2.iris")
#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1          5.1         3.5          1.4         0.2  setosa
#> 2          4.9         3.0          1.4         0.2  setosa
#> 3          4.7         3.2          1.3         0.2  setosa
#> 4          4.6         3.1          1.5         0.2  setosa
#> 5          5.0         3.6          1.4         0.2  setosa
#> 6          5.4         3.9          1.7         0.4  setosa

reprex package (v0.3.0)

于 2019-10-08 创建