如何使用没有`collect()`的`dplyr`将数据附加到PostgreSQL表?

时间:2016-07-26 14:09:23

标签: r postgresql dplyr rpostgresql

reg_data是PostgreSQL表。事实证明,在PostgreSQL中运行回归更快。但是,当我运行数十万个数据集时,我希望按数据集执行数据集并将每个数据的结果附加到表中。

有没有办法使用原生dplyr动词将PostgreSQL数据附加到PostgreSQL表?我不确定将数据带到R然后将它们发送回PostgreSQL(只有6个数字和几个识别字段)需要付出巨大的代价,但它确实看起来不够优雅。

library(dplyr)

pg <- src_postgres()

reg_data <- tbl(pg, "reg_data")

reg_results <-
    reg_data %>%
    summarize(r_squared=regr_r2(y, x),
              num_obs=regr_count(y, x),
              constant=regr_intercept(y, x),
              slope=regr_slope(y, x),
              mean_analyst_fog=regr_avgx(y, x),
              mean_manager_fog=regr_avgy(y, x)) %>%
    collect() %>%
    as.data.frame()

# Push to database.
dbWriteTable(pg$con, c("bgt", "within_call_data"), reg_results,
             append=TRUE, row.names=FALSE)

2 个答案:

答案 0 :(得分:4)

dplyr不包含在数据库中插入或更新记录的命令,因此没有完整的原生dplyr解决方案。但是你可以将dplyr与常规SQL语句结合起来,以避免将数据带到R.

让我们从collect()陈述

之前的步骤开始
library(dplyr)

pg <- src_postgres()

reg_data <- tbl(pg, "reg_data")

reg_results <-
    reg_data %>%
    summarize(r_squared=regr_r2(y, x),
              num_obs=regr_count(y, x),
              constant=regr_intercept(y, x),
              slope=regr_slope(y, x),
              mean_analyst_fog=regr_avgx(y, x),
              mean_manager_fog=regr_avgy(y, x))

现在,您可以使用compute()代替collect()在数据库中创建临时表。

temp.table.name <- paste0(sample(letters, 10, replace = TRUE), collapse = "")

reg_results <- reg_results %>% compute(name=random.table.name)

其中temp.table.name是随机表名称。在计算中使用选项name= temp.table.name,我们将此随机名称分配给创建的临时表。

现在,我们将使用库RPostgreSQL创建一个插入查询,该查询使用临时表中存储的结果。由于临时表仅存在于src_postgresql()创建的连接中,我们需要重用它。

library(RPostgreSQL)
copyconn <- pg$con
class(copyconn) <- "PostgreSQLConnection" # I get an error if I don't fix the class

最后插入查询

sql <- paste0("INSERT INTO destination_table SELECT * FROM ", temp.tbl.name,";")

dbSendQuery(copyconn, sql)

因此,一切都在数据库中发生,数据不会被带入R。

修改

当我们从temp.tbl.name获取reg_results时,此帖子的早期版本确实打破了封装。使用计算中的name=选项可以避免这种情况。

答案 1 :(得分:2)

另一种选择是使用一个名为sql_render()的命令来创建每个SQL语句,然后使用另一个名为db_save_query()的命令来使用SQL语句创建表,然后使用手动语句来附加到表。要遍历每个查询,请使用purrr命令:mapwalk。优选地,像compute()命令这样的命令应该这样做,但是代替它,以下是完全可重现的示例:

library(dplyr)
library(dbplyr)
library(purrr)

# Setting up a SQLite db with 3 tables
con <- DBI::dbConnect(RSQLite::SQLite(), path = ":memory:")
copy_to(con, filter(mtcars, cyl == 4), "mtcars1")
copy_to(con, filter(mtcars, cyl == 6), "mtcars2")
copy_to(con, filter(mtcars, cyl == 8), "mtcars3")



# Pre-process the SQL statements
tables <- c("mtcars1","mtcars2","mtcars3")
all_results <- tables %>%
  map(~{
    tbl(con, .x) %>%
      summarise(avg_mpg = mean(mpg),
                records = n()) %>%
      sql_render() 
  })

# Execute the SQL statements, 1st one creates the table
# subsquent queries are insterted to the table
first_query <- TRUE
all_results %>%
  walk(~{
    if(first_query == TRUE){
      first_query <<- FALSE
      db_save_query(con, ., "results")
    } else {
      dbExecute(con, build_sql("INSERT INTO results ", .))
    }
  })


tbl(con, "results")

dbDisconnect(con)