上面 manotheshark 的函数非常有用,但我想使用 SQL 查询而不是表名来访问数据库,并将数据库名称作为参数传递,因为我通常使用多个 Access数据库。这是修改后的版本:
access_sql_32 <- function(db_sql = NULL, table_out = NULL, db_path = NULL) {
library(svSocket)
# variables to make values uniform
sock_port <- 8642L
sock_con <- "sv_con"
ODBC_con <- "a32_con"
if (file.exists(db_path)) {
# build ODBC string
ODBC_str <- local({
s <- list()
s$path <- paste0("DBQ=", gsub("(/|\\\\)+", "/", path.expand(db_path)))
s$driver <- "Driver={Microsoft Access Driver (*.mdb, *.accdb)}"
s$threads <- "Threads=4"
s$buffer <- "MaxBufferSize=4096"
s$timeout <- "PageTimeout=5"
paste(s, collapse=";")
})
# start socket server to transfer data to 32 bit session
startSocketServer(port=sock_port, server.name="access_query_32", local=TRUE)
# build expression to pass to 32 bit R session
expr <- "library(svSocket)"
expr <- c(expr, "library(RODBC)")
expr <- c(expr, sprintf("%s <- odbcDriverConnect('%s')", ODBC_con, ODBC_str))
expr <- c(expr, sprintf("%1$s <- sqlQuery(%3$s, \"%2$s\")", table_out, db_sql, ODBC_con))
expr <- c(expr, sprintf("%s <- socketConnection(port=%i)", sock_con, sock_port))
expr <- c(expr, sprintf("evalServer(%s, %s, %s)", sock_con, table_out, table_out))
expr <- c(expr, "odbcCloseAll()")
expr <- c(expr, sprintf("close(%s)", sock_con))
expr <- paste(expr, collapse=";")
# launch 32 bit R session and run the expression we built
prog <- file.path(R.home(), "bin", "i386", "Rscript.exe")
system2(prog, args=c("-e", shQuote(expr)), stdout=NULL, wait=TRUE, invisible=TRUE)
# stop socket server
stopSocketServer(port=sock_port)
# display table fields
message("Retrieved: ", table_out, " - ", paste(colnames(get(table_out)), collapse=", "))
} else {
warning("database not found: ", db_path)
}
}
我在弄清楚如何调用 manotheshark 的函数时也遇到了一些困难,我花了一些时间研究 svSocket 包文档才意识到调用脚本需要实例化返回数据的对象,然后传递它的 NAME (不是对象本身)在 table_out 参数中。这是一个调用我修改后的版本的 R 脚本示例:
source("scripts/access_sql_32.R")
spnames <- data.frame()
# NB. use single quotes for any embedded strings in the SQL
sql <- "SELECT name as species FROM checklist
WHERE rank = 'species' ORDER BY name"
access_sql_32(sql, "spnames", "X:/path/path/mydata.accdb")
这可行,但有局限性。
首先,避免任何 Microsoft Access SQL 扩展。例如,如果您使用“访问查询”构建器,它通常会插入像 [TABLE_NAME]![FIELD_NAME] 这样的字段名称。这些都行不通。 Access 还允许以“10kmSq”之类的数字开头的非标准字段名称,并允许您在 SQL 中使用它们,例如SELECT [10kmSq] FROM ...。这也行不通。如果 SQL 语法有错误,返回变量将包含错误消息。
其次,您可以返回的数据量似乎限制为 64Kb。如果您尝试运行返回过多的 SQL,则 32 位会话不会终止并且脚本会挂起。