14

使用RODBCsqlUpdate(channel, dat, ...) ,您可以通过类似的函数,dat = data.frame(...)而不必构建自己的 SQL 字符串。

但是,使用 R 的DBI,我看到的只是函数dbSendQuery(conn, statement, ...),它们只接受一个字符串statement并且没有机会data.frame直接指定 a 。

那么如何UPDATE使用data.framewith DBI 呢?

4

3 回答 3

15

真的晚了,我的回答,但也许仍然有帮助......

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

于 2017-04-26T19:06:58.173 回答
2

基于 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))
}

在哪里

  • x:包含 1+ 个关键列和 1+ 个更新列的 1 行数据框。
  • key_cols:字符向量,包含 1 个或多个作为键的列名(即在 WHERE 子句中使用)
于 2020-07-18T00:05:31.590 回答
1

这是我使用 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")
于 2021-01-15T22:33:00.440 回答