Database 如何使用R DBI传递data.frame进行更新

Database 如何使用R DBI传递data.frame进行更新,database,r,dataframe,rodbc,r-dbi,Database,R,Dataframe,Rodbc,R Dbi,使用,有一些函数,如sqlUpdate(channel,dat,…),允许您传递dat=data.frame(…),而不必构造自己的SQL字符串 然而,对于R,我看到的只是像dbSendQuery(conn,statement,…)这样的函数,它只接受字符串语句,没有机会直接指定data.frame 那么,如何使用带有DBI的data.frame更新呢?我的答案是,真的很晚了,但可能还是有帮助的 DBI/odbc包中没有单个函数(我知道),但您可以使用准备好的update语句复制更新行为(该语句

使用,有一些函数,如
sqlUpdate(channel,dat,…)
,允许您传递
dat=data.frame(…)
,而不必构造自己的SQL字符串

然而,对于R,我看到的只是像
dbSendQuery(conn,statement,…)
这样的函数,它只接受字符串
语句
,没有机会直接指定
data.frame


那么,如何使用带有DBI的
data.frame
更新
呢?

我的答案是,真的很晚了,但可能还是有帮助的

DBI/odbc包中没有单个函数(我知道),但您可以使用准备好的update语句复制更新行为(该语句应该比RODBC的
sqlUpdate
更快,因为它将参数值作为批发送到SQL server:

library(DBI)
library(odbc)

con <- dbConnect(odbc::odbc(), driver="{SQL Server Native Client 11.0}", server="dbserver.domain.com\\default,1234", Trusted_Connection = "yes", database = "test")  # assumes Microsoft SQL Server

dbWriteTable(con, "iris", iris, row.names = TRUE)      # create and populate a table (adding the row names as a separate columns used as row ID)

update <- dbSendQuery(con, 'update iris set "Sepal.Length"=?, "Sepal.Width"=?, "Petal.Length"=?, "Petal.Width"=?, "Species"=? WHERE row_names=?')

# create a modified version of `iris`
iris2 <- iris
iris2$Sepal.Length <- 5
iris2$Petal.Width[2] <- 1
iris2$row_names <- rownames(iris)  # use the row names as unique row ID

dbBind(update, iris2)  # send the updated data

dbClearResult(update)  # release the prepared statement

# now read the modified data - you will see the updates did work
data1 <- dbReadTable(con, "iris")

dbDisconnect(con)
库(DBI)
数据库(odbc)

con基于R Yoda的答案,我将自己设置为下面的助手函数。这允许使用数据框指定更新条件

虽然我构建它是为了运行事务更新(即单行),但理论上它可以通过一个条件更新多行。但是,这与使用输入数据帧更新多行不同。也许其他人可以在此基础上构建


dbUpdateCustom=函数(x,键列,con,模式名,表名){
如果(nrow(x)!=1)停止(“输入数据帧必须正好是1行”)
如果(!all(key\u cols%在%colnames(x)中))停止(“key\u cols”中指定的所有列必须出现在“x”中)
#生成更新字符串--------------------------------------------------

df_key这里是我使用REPLACE-INTO组合起来的一个小助手函数,它使用DBI更新一个表,用新值替换旧的重复条目。它是基本的,适合我自己的需要,但应该很容易修改。您需要传递给该函数的只是连接、表名和数据帧。请注意,该表必须有一个主表我还包括了一个简单的工作示例

row_to_list <- function(Y)  suppressWarnings(split(Y, f = row(Y)))

sql_val <- function(y){
  if(!is.numeric(y)){
    return(paste0("'",y,"'"))
  }else{
    if(is.na(y)){
      return("NULL")
    }else{
      return(as.character(y))
    }
  }
}

to_sql_row <- function(x) paste0("(",paste(do.call("c", lapply(x, FUN = sql_val)), collapse = ", "),")")

bracket <- function(x) paste0("`",x,"`")

to_sql_string <- function(x) paste0("(",paste(sapply(x, FUN = bracket), collapse = ", "),")")

replace_into_table <- function(con, table_name, new_data){
  #new_data <- data.table(new_data)
  cols <- to_sql_string(names(new_data))
  vals <- paste(lapply(row_to_list(new_data), FUN = to_sql_row), collapse = ", ")
  query <- paste("REPLACE INTO", table_name, cols, "VALUES", vals)
  rs <- dbExecute(con, query)
  return(rs)
}

tb <- data.frame("id" = letters[1:20], "A" = 1:20, "B" = seq(.1,2,.1)) # sample data
dbWriteTable(con, "test_table", tb) # create table
dbExecute(con, "ALTER TABLE test_table ADD PRIMARY KEY (id)") # set primary key

new_data <- data.frame("id" = letters[19:23], "A" = 1:5, "B" = seq(101,105)) # new data
new_data[4,2] <- NA # add some NA values
new_data[5,3] <- NA
table_name <- "test_table"
replace_into_table(con, "test_table", new_data)

result <- dbReadTable(con, "test_table")

行到行列表谢谢,这适用于
插入
,但是如何执行
更新
?谢谢。我们尝试一次更新的行数有限制吗?我创建了一个执行合并的长sql语句,如果该语句太长,则
dbGetQuery
返回0(受影响的0行)没有简单的答案,因为DBI包只定义了一个接口,并将实现留给不同的DBI兼容包。最终,所有限制都取决于符合DBI的驱动程序包(例如,
odbc
)数据库加上DBI驱动程序包使用的二进制DB驱动程序。是否使用
odbc
包?那么最好的方法是创建一个可复制的代码示例和数据库,并打开一个问题(要求修复或至少记录限制)在@RockScience,由于您描述的类似问题,我已经启动了一个DBI合规性检查“项目”在这里,我针对不同的DBI配置运行
DBItest
单元测试,以估计每个DBI配置的成熟度、开放问题和限制。状态仍然是pre-alpha,我没有发布结果或从结果中得出任何结论,但您可以在(当然,试着根据您自己的配置运行测试)谢谢,非常令人印象深刻的工作!我一定会看一看。从我目前使用odbc和SQL Server看到的情况来看,有大量(>30k行)指定源值的合并失败,我必须发送到一个临时表,然后执行表之间的合并。我将检查如何在测试中进行翻译。如果您在此处发布一个新问题并举例说明错误,我将对此进行研究(请在此处添加链接,以便我可以找到您的问题)