目前要在PostgreSQL表中插入数据,我必须创建一个空表,然后将insert into table values ...
和一个数据框折叠,然后执行包含所有值的单个字符串。它不适用于大型数据帧。
dbWtriteTable()
对PostgreSQL不起作用,并出现以下错误......
Error in postgresqlpqExec(new.con, sql4) : RS-DBI driver: (could not Retrieve the result : ERROR: syntax error at or near "STDIN" LINE 1: COPY "table_1" FROM STDIN
我在回答之前提出的类似问题时已经尝试了以下黑客攻击。这是链接... How do I write data from R to PostgreSQL tables with an autoincrementing primary key?
body_lines <- deparse(body(RPostgreSQL::postgresqlWriteTable))
new_body_lines <- sub(
'postgresqlTableRef(name), "FROM STDIN")',
'postgresqlTableRef(name), "(", paste(shQuote(names(value)), collapse = ","), ") FROM STDIN")',
body_lines,
fixed = TRUE
)
fn <- RPostgreSQL::postgresqlWriteTable
body(fn) <- parse(text = new_body_lines)
while("RPostgreSQL" %in% search()) detach("package:RPostgreSQL")
assignInNamespace("postgresqlWriteTable", fn, "RPostgreSQL")
这个黑客对我来说仍然不起作用。 postgresqlWriteTable()
抛出完全相同的错误......
究竟是什么问题?
作为替代方案,我尝试使用dbWriteTable2()
包中的caroline
。它会引发不同的错误...
Error in postgresqlExecStatement(conn, statement, ...) :
RS-DBI driver: (could not Retrieve the result : ERROR: column "id" does not exist in table_1
)
creating NAs/NULLs for for fields of table that are missing in your df
Error in postgresqlExecStatement(conn, statement, ...) :
RS-DBI driver: (could not Retrieve the result : ERROR: column "id" does not exist in table_1
)
还有其他方法可以直接将大型数据帧写入PostgreSQL中的表吗?
答案 0 :(得分:1)
好的,我不确定为什么dbWriteTable()
会失败;可能存在某种版本/协议不匹配。如果可能的话,也许您可以尝试安装最新版本的R,RPostgreSQL包,以及升级系统上的PostgreSQL服务器。
关于大数据失败的insert into
解决方法,当必须移动大量数据并且一次性转移不可行/不切实际/片状时,在IT世界中经常做什么有时被称为作为批处理或batch processing。基本上,您将数据分成较小的块并一次发送一个块。
作为一个随机的例子,几年前我写了一些Java代码来查询来自HR LDAP服务器的员工信息,该服务器被限制为一次只提供1000条记录。所以基本上我必须写一个循环来继续发送相同的请求(使用some kind of weird cookie-based mechanism跟踪查询状态)并将记录累积到本地数据库,直到服务器报告查询完成。
这是一些代码,它们手动构造SQL以基于给定的data.frame创建空表,然后使用参数化批量大小将data.frame的内容插入到表中。它主要围绕调用paste()
来构建SQL字符串,dbSendQuery()
来发送实际查询。我还使用postgresqlDataType()
来创建表。
## connect to the DB
library('RPostgreSQL'); ## loads DBI automatically
drv <- dbDriver('PostgreSQL');
con <- dbConnect(drv,host=...,port=...,dbname=...,user=...,password=...);
## define helper functions
createEmptyTable <- function(con,tn,df) {
sql <- paste0("create table \"",tn,"\" (",paste0(collapse=',','"',names(df),'" ',sapply(df[0,],postgresqlDataType)),");");
dbSendQuery(con,sql);
invisible();
};
insertBatch <- function(con,tn,df,size=100L) {
if (nrow(df)==0L) return(invisible());
cnt <- (nrow(df)-1L)%/%size+1L;
for (i in seq(0L,len=cnt)) {
sql <- paste0("insert into \"",tn,"\" values (",do.call(paste,c(sep=',',collapse='),(',lapply(df[seq(i*size+1L,min(nrow(df),(i+1L)*size)),],shQuote))),");");
dbSendQuery(con,sql);
};
invisible();
};
## generate test data
NC <- 1e2L; NR <- 1e3L; df <- as.data.frame(replicate(NC,runif(NR)));
## run it
tn <- 't1';
dbRemoveTable(con,tn);
createEmptyTable(con,tn,df);
insertBatch(con,tn,df);
res <- dbReadTable(con,tn);
all.equal(df,res);
## [1] TRUE
请注意,我没有费心将row.names
列添加到数据库表中,这与dbWriteTable()
不同,private int previousLength;
private boolean backSpace;
// ...
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
previousLength = s.length();
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
backSpace = previousLength > s.length();
if (backSpace) {
// do your stuff ...
}
}
似乎总是包含这样的列(并且似乎没有提供任何预防方法它)。
答案 1 :(得分:1)
在处理this example时遇到相同的错误。
为我工作:
dbWriteTable(con, "cartable", value = df, overwrite = T, append = F, row.names = FALSE)
虽然我在pgAdmin中配置了一个表“ cartable”。因此,存在一个空表,我不得不用值覆盖该表。
答案 2 :(得分:0)
因此,前面给出的显示批处理的答案是99.99%正确。但是,由于在'insertBatch'函数中需要一个很小的参数,因此它在Windows上不起作用。 (无法为同一答案添加评论)
'shQuote'函数需要一个参数类型='cmd2'才能起作用。
但是,要在此处添加参数,您需要以下答案:
[https://stackoverflow.com/questions/6827299/r-apply-function-with-multiple-parameters] [1]
因此,新的“ insertBatch”功能变为:
insertBatch <- function(con,tn,df,size=100L) {
if (nrow(df)==0L) return(invisible());
cnt <- (nrow(df)-1L)%/%size+1L;
for (i in seq(0L,len=cnt)) {
sql <- paste0("insert into \"",tn,"\" values (",do.call(paste,c(sep=',',collapse='),(',lapply(df[seq(i*size+1L,min(nrow(df),(i+1L)*size)),],shQuote,type = 'cmd2'))),");");
dbSendQuery(con,sql);
};
invisible();
};