使用RODBCsqlUpdate(channel, dat, ...)
,您可以通过类似的函数,dat = data.frame(...)
而不必构建自己的 SQL 字符串。
但是,使用 R 的DBI,我看到的只是函数dbSendQuery(conn, statement, ...)
,它们只接受一个字符串statement
并且没有机会data.frame
直接指定 a 。
那么如何UPDATE
使用data.frame
with DBI 呢?
真的晚了,我的回答,但也许仍然有帮助......
DBI/odbc 包中没有单个函数(我知道),但您可以使用准备好的更新语句复制更新行为(它应该比 RODBC 更快,sqlUpdate
因为它将参数值作为批处理发送到 SQL 服务器:
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)
仅当您有一个主键时才有效,该主键是我在上面的示例中通过使用行名创建的,该行名是每行增加一的唯一数字...
odbc
有关我在 DBIdbConnect
语句中使用的包的更多信息,请参见: https ://github.com/rstats-db/odbc
基于 R Yoda 的回答,我将自己设置为下面的辅助函数。这允许使用数据框来指定更新条件。
虽然我构建它是为了运行事务更新(即单行),但理论上它可以通过条件更新多行。但是,这与使用输入数据框更新多行不同。也许其他人可以在此基础上建立...
dbUpdateCustom = function(x, key_cols, con, schema_name, table_name) {
if (nrow(x) != 1) stop("Input dataframe must be exactly 1 row")
if (!all(key_cols %in% colnames(x))) stop("All columns specified in 'key_cols' must be present in 'x'")
# Build the update string --------------------------------------------------
df_key <- dplyr::select(x, one_of(key_cols))
df_upt <- dplyr::select(x, -one_of(key_cols))
set_str <- purrr::map_chr(colnames(df_upt), ~glue::glue_sql('{`.x`} = {x[[.x]]}', .con = con))
set_str <- paste(set_str, collapse = ", ")
where_str <- purrr::map_chr(colnames(df_key), ~glue::glue_sql("{`.x`} = {x[[.x]]}", .con = con))
where_str <- paste(where_str, collapse = " AND ")
update_str <- glue::glue('UPDATE {schema_name}.{table_name} SET {set_str} WHERE {where_str}')
# Execute ------------------------------------------------------------------
query_res <- DBI::dbSendQuery(con, update_str)
DBI::dbClearResult(query_res)
return (invisible(TRUE))
}
在哪里
这是我使用 REPLACE INTO 组合在一起的一个小辅助函数,用于使用 DBI 更新表,用新值替换旧的重复条目。它是基本的,适合我自己的需要,但应该很容易修改。您需要传递给函数的只是连接、表名和数据框。请注意,该表必须具有 PRIMARY KEY 列。我还提供了一个简单的工作示例。
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")