Sql 使用dataframes和dbSendQuery更新表

Sql 使用dataframes和dbSendQuery更新表,sql,r,Sql,R,我在数据库中查询了一个表,将值存储在数据框中,然后对它们进行操作。这是我查询数据的代码: #Setup Connection con1 <- dbConnect(odbc::odbc(), "XXXX", database="XXXX") r1 <- dbSendQuery(con1, " select pcd, oseast1m, osnrth1m from onspd as ons where ons.pcd like 'bt%' and oseast1m != '' ") re

我在数据库中查询了一个表,将值存储在数据框中,然后对它们进行操作。这是我查询数据的代码:

#Setup Connection
con1 <- dbConnect(odbc::odbc(), "XXXX", database="XXXX")
r1 <- dbSendQuery(con1, "
select pcd, oseast1m, osnrth1m from onspd as ons where ons.pcd like 'bt%' and oseast1m != ''
")
result <- dbFetch(r1)
现在,我想用如下方法将值从dataframe写回数据库:

dbClearResult(r1)
sql <- "
update ons
set ons.oseast1m=?east, os.osnrth1m=?west
from ONS_TEST as ons where ons.Postcode=?post
"
r1 <- dbSendQuery(con1, sqlInterpolate(ANSI(), sql, east = result$oseast1m, west = result$osnrth1m, post = result$pcd))
这给了我一个错误的值必须是长度1,这显然是不正确的我想要的

运行更新的语法是什么?或者我需要写一个for循环来实现同样的事情吗

谢谢


mike

我认为您有两个选择:1个向上插入,或2个参数化查询

第一种方法通常具有速度优势,这取决于DBMS,但代价是DBMS特有的SQL方言和一点复杂性。第二种方法的优点是简单,但如果有许多行,则可能需要更长的时间

1.向上插入 步骤:创建一个临时表见备注;上传数据;使用冲突解决方案执行更新操作

我在这里使用temp_table997作为临时表,但是有很多方法可以处理temp tables,这样您就不会意外地将它留下。我发现这种方法的成功与否因DBMS而异,所以我将把它留给读者

DBI::dbExecutecon, 创建表格temp_TABLE_997作为 选择oseast1m,osnrth1m,从ons限制0中选择邮政编码[1,2] DBI::dbWriteTablecon,临时表格_997,结果[,停止,西部,邮政编码][3,4] DBI::dbExecutecon, 插入ons OSAST1M、osnrth1m 选择oseast1m、osnrth1m 来自临时表格997 论冲突邮政编码 更新集oseast1m=EXCLUDED.oseast1m,osnrth1m=EXCLUDED.osnrth1m [5] 注:

采用此技术的其他答案/文章可能会使用select*…,尽管最佳实践不鼓励这样做。通常情况下,更好的方法是显式地使用表和必要的字段

我使用创建表。。。作为选择。。。以便保留列类型。特别是各种类型的浮点数、整数、bigint、smallint、偶位和其他字段。。。事实上,R并没有达到这个粒度级别,我发现在上传数据时最好是显式的。使用此技术可以确保目标表中使用的类型是实际使用的类型。在某些DBMS中可能不需要这样做,但我认为这样做不会有什么坏处

与注1类似,您应该只上载需要的列,包括标识字段和带有更新的字段;如果存在永不更新的字段,则没有理由浪费带宽,而且在较大的数据集上,这会对上载时间产生相当大的影响。例如,结果[,cPostcode,…]

虽然我使用的工具和数据库足够聪明,可以处理无序的列,但我不知道所有数据库管理系统都是如此,所以保持列的顺序不变可能是最好的,也很容易

我推断邮政编码在表格中是完全唯一的。它不一定是单独讨论的表中的键,但假设该字段唯一标识行。如果不是,则上述查询可能会影响比预期更多的行

这在SQLite和Postgres上适用,但其他DBMS的说法可能相同或非常相似

对于SQL Server,您需要一个稍微不同的查询。认识到,创造。。。选择需要创建上面的限制0。。。由于SQL Server的方言,请选择顶部0

DBI::dbExecutecon, 声明@dummy int; 将ons与HOLDLOCK合并为tgt 使用选择。。。来自临时表格997,作为src 关于tgt.邮政编码 匹配后,更新集tgt.oseast1m=src.oseast1m,tgt.osnrth1m=src.osnrth1m 如果不匹配,则插入oseast1m、oseast1m值src.oseast1m、src.oseast1m 如果使用此方法,请不要忘记清理:

DBI::dbExecutecon,drop table temp_table_997 2.绑定参数化查询 如果只有少数几行,或者你真的看不到这样做的时间惩罚,那么试试这个


@saae的res评论:set-ons.oseast1m=?east,os.osnrth1m=?west中可能有拼写错误,应该是set-ons.oseast1m=?east,ons.osnrth1m=?west?sqlInterpolate用于简单的长度1绑定。我建议您尝试在SQL中执行upsert操作。通常最好上载到临时表,然后从临时表向上插入原始表。由于每个DBMS通常都有一个稍微不同的机制来处理是否存在冲突,因此了解您的DBMS是很有用的。谢谢您的详细回答和解释。这真的很有帮助。我使用了dbBind方法,它完全按照预期工作。很好的结果。谢谢你说得这么清楚!顺便说一句,数据库绑定非常慢。。。使用dbExecute上传行的子集,然后对所讨论的表进行更新确实非常快。两个伟大的解决方案。谢谢