问题
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