如何在R中编写PostgreSQL中的表?

时间:2016-06-06 07:11:33

标签: r postgresql

目前要在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中的表吗?

3 个答案:

答案 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();
};