In R connected to a database through ODBC, how do I disconnect after running one query to send another query without cancelling the previous?

若如初见. 提交于 2021-01-28 07:11:22

问题


I am new to R and new to Stack Overflow, so apologies if I don't post this correctly! I have R linked to an Access database using the ODBC and DBI packages. I scoured the internet and couldn't find a way to write an update query, so I've created a loop that manually pretty much does the same thing. The problem is that to do this, I am using the dbSendStatement function. It seems that when you use that function you need a way to then end it before moving onto another query, or else you get this error:

In new_result(connection@ptr, statement, immediate) :
  Cancelling previous query

How do I complete one query so I can move onto the next without canceling the last?

Here is the part of my code I think is relevant:

require(DBI)
require (odbc)

dB.Connection = dbConnect(odbc::odbc(), dsn = "Development Database")

BLDG.LVL.Details = data.frame()

for (i in 2:length(Temp.File.List)){
  Temp.BLDG.LVL = read_excel(Temp.File.List[i], range = cell_cols("A:O"))
  BLDG.LVL.Details = rbind(BLDG.LVL.Details, Temp.BLDG.LVL)
  file.move(Temp.File.List[i],paste("C:/UserData/",Sys.getenv("USERNAME"),"/OneDrive - Siemens AG/Development Database/06 FIM (BLDG LVL)/Archive", sep = ""))}

for (i in 1:nrow(BLDG.LVL.Details)){
  DBI::dbSendStatement(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Included = ",BLDG.LVL.Details[i,3]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))
  DBI::dbSendStatement(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Proposed_kW_Yr1 = ",BLDG.LVL.Details[i,4]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))
  DBI::dbSendStatement(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Proposed_kWh_Yr1 = ",BLDG.LVL.Details[i,6]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))
  DBI::dbSendStatement(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Proposed_NGas_Yr1 = ",BLDG.LVL.Details[i,7]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))
  DBI::dbSendStatement(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Proposed_H2O_Yr1 = ",BLDG.LVL.Details[i,8]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))
  DBI::dbSendStatement(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Proposed_FuelOil_Yr1 = ",BLDG.LVL.Details[i,9]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))
  DBI::dbSendStatement(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Proposed_Propane_Yr1 = ",BLDG.LVL.Details[i,10]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))
  DBI::dbSendStatement(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Proposed_Other_Yr1 = ",BLDG.LVL.Details[i,11]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))
  DBI::dbSendStatement(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Operational_Savings_USD_BLDG = ",BLDG.LVL.Details[i,13]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))
  DBI::dbSendStatement(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Operational_Savings_Yrs_BLDG = ",BLDG.LVL.Details[i,14]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))
  DBI::dbSendStatement(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Operational_Savings_Persistence_BLDG = ",BLDG.LVL.Details[i,15]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))
  DBI::dbSendStatement(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Included = ",BLDG.LVL.Details[i,3]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))}



回答1:


dbSendStatement() returns a result object, you need to clear it with dbClearResult(). It's easier to send the query with dbExecute() instead, basically this is a combination of dbSendStatement() and dbClearResult().

for (i in 1:nrow(BLDG.LVL.Details)){
  DBI::dbExecute(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Included = ",BLDG.LVL.Details[i,3]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))
  DBI::dbExecute(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Proposed_kW_Yr1 = ",BLDG.LVL.Details[i,4]," WHERE FIM_ID = ",BLDG.LVL.Details[i,1]," AND BUILDING_ID = ",BLDG.LVL.Details[i,2],";", sep = ""))

There are a few ways to improve this:

  • Use dbQuoteLiteral() to quote literal values and protect against SQL injection, this also solves In R connected to an Access Database through ODBC, how can I update a text field?:

    for (i in 1:nrow(BLDG.LVL.Details)){
      DBI::dbExecute(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Included = ",DBI::dbQuoteLiteral(conn,BLDG.LVL.Details[i,3])," WHERE FIM_ID = ",DBI::dbQuoteLiteral(conn,BLDG.LVL.Details[i,1])," AND BUILDING_ID = ",DBI::dbQuoteLiteral(conn,BLDG.LVL.Details[i,2]),";", sep = ""))
      DBI::dbExecute(conn = dB.Connection, statement = paste("UPDATE DC_FIMs_BLDG_Lvl SET Proposed_kW_Yr1 = ",DBI::dbQuoteLiteral(conn,BLDG.LVL.Details[i,4])," WHERE FIM_ID = ",DBI::dbQuoteLiteral(conn,BLDG.LVL.Details[i,1])," AND BUILDING_ID = ",DBI::dbQuoteLiteral(conn,BLDG.LVL.Details[i,2]),";", sep = ""))
    
  • Use parameters to pass the query parameters safely (unfortunately the placeholder syntax varies across DBMS, ? might just work):

    for (i in 1:nrow(BLDG.LVL.Details)){
      DBI::dbExecute(conn = dB.Connection, statement = "UPDATE DC_FIMs_BLDG_Lvl SET Included = ? WHERE FIM_ID = ? AND BUILDING_ID = ?;", params = list(BLDG.LVL.Details[i,3],BLDG.LVL.Details[i,1],BLDG.LVL.Details[i,2]))
      ...
    
  • Risking to state the obvious, updating more than one column in the same query will be much faster, with or without placeholders:

    for (i in 1:nrow(BLDG.LVL.Details)){
      DBI::dbExecute(conn = dB.Connection, statement = "UPDATE DC_FIMs_BLDG_Lvl SET Included = ?, Proposed_kW_Yr1 = ? WHERE FIM_ID = ? AND BUILDING_ID = ?;", params = list(BLDG.LVL.Details[i,3],BLDG.LVL.Details[i,4],BLDG.LVL.Details[i,1],BLDG.LVL.Details[i,2]))
      ...
    
  • I believe you can replace your entire loop with something of the form:

    library(dm)
    
    ...
    
    tbl <- dplyr::tbl(conn, "DC_FIMs_BLDG_Lvl")
    dplyr::rows_update(tbl, BLDG.LVL.Details, by = c("FIM_ID", "BUILDING_ID"), in_place = TRUE, copy = TRUE)
    

    The functionality requires the dm package to be loaded. Read more at https://krlmlr.github.io/dm/articles/howto-dm-rows.html -- that text shows a different approach that is useful if you are accessing multiple tables on a database. Let me know if it works for you.

  • Take a look at the dbx package for sending update queries, does it work for you?



来源:https://stackoverflow.com/questions/64971555/in-r-connected-to-a-database-through-odbc-how-do-i-disconnect-after-running-one

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!