【问题标题】:How to write a table in PostgreSQL from R?如何从 R 在 PostgreSQL 中编写表?
【发布时间】:2016-06-06 07:11:33
【问题描述】:

目前要在 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() 抛出完全相同的错误...... 这到底是什么问题?

作为替代方案,我尝试使用来自caroline 包的dbWriteTable2()。它会引发不同的错误...

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中的表?

【问题讨论】:

  • 我们在这里讨论的规模有多大?我刚刚使用 RPostgresQL::dbWriteTable() 成功地将 R 中的 ~800MB 10,000x1,000 data.frame 写入 PostgreSQL 表。花了一段时间(我想大约是一个小时),但它奏效了。
  • @bgoldst 我的意思是 insert into 方法对于大数据失败... dbWriteTable() 即使对于小数据 (1X1 df) 也失败并给出错误说明

标签: r postgresql


【解决方案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() 不同,dbWriteTable() 似乎总是包含这样一个列(并且似乎没有提供任何阻止它的方法)。

【讨论】:

  • 最后请注意:dbWriteTable(..., row.names=F)
【解决方案2】:

我在处理 this example 时遇到了同样的错误。

为我工作:

dbWriteTable(con, "cartable", value = df, overwrite = T, append = F, row.names = FALSE)

虽然我在 pgAdmin 中配置了一个表“cartable”。所以存在一个空表,我必须用值覆盖该表。

【讨论】:

    【解决方案3】:

    所以前面给出的批处理的答案是 99.99% 正确的。但是,它在 Windows 上不起作用,因为“insertBatch”函数需要一个很小的参数。 (无法为相同的答案添加评论)

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-24
      • 1970-01-01
      • 1970-01-01
      • 2017-12-06
      相关资源
      最近更新 更多