仅在查询返回少于n_max行时收集

时间:2017-11-03 14:02:17

标签: r dplyr r-dbi roracle dbplyr

偶尔通过OracleROracle连接到我的dbplyr数据库时,我将运行dplyr::collect操作,该操作获取的数据超出预期,而且R可以处理。

这可能导致R崩溃,并且通常是我应该在获取之前进一步过滤或聚合数据的标志。

能够在选择是否获取结果之前检查结果的大小会很棒(不运行查询两次)。

我们将collect2的变体命名为collect,允许这样做:

预期行为:

small_t <- con %>% tbl("small_table") %>%
  filter_group_etc %>%
  collect2(n_max = 5e6) # works fine

big_t   <- con %>% tbl("big_table")   %>%
  filter_group_etc %>%
  collect2(n_max = 5e6) # Error: query returned 15.486.245 rows, n_max set to 5.000.000

这可能吗?

我也对使用ROracle / DBI而不是dplyr的解决方案持开放态度,例如:

dbGetQuery2(con, my_big_sql_query,n_max = 5e6) # Error: query returned 15.486.245 rows, n_max set to 5.000.000

修改

见下面作为答案发布的部分解决方案,不是最佳的,因为浪费了一些时间来获取我没有用的数据。

4 个答案:

答案 0 :(得分:6)

这并没有解决你在关于花费资源来获取查询两次的评论中提到的问题,但它似乎确实有效(至少对我的MySQL数据库 - 我没有Oracle数据库测试它):

collect2  <- function(query, limit = 20000) {

  query_nrows  <- query %>% 
    ungroup() %>% 
    summarize(n = n()) %>% 
    collect() %>% 
    pull('n')


  if(query_nrows <= limit) {
    collect(query)
  } else {
    warning("Query has ", query_nrows,"; limit is ", limit,". Data will not be collected.")
  }

}

在没有实际运行查询的情况下,我没有看到任何方法来测试查询结果中的行数。但是,使用这种方法,您总是先强制计算行号,然后在超过20,000(或行数限制为止)时拒绝收集。

答案 1 :(得分:1)

您实际上可以在一个SQL查询

中实现目标

使用dplyr的 mutate 而不是汇总,将行数(n)作为额外列添加到数据中,然后设置 n&lt; n_limit作为过滤条件。此条件对应于SQL中的having子句。如果行计数大于列表,则不会收集任何数据。否则收集所有数据。您可能希望最后删除行计数列。

这种方法适用于大多数数据库。我已经使用PostgreSQL和Oracle验证了这一点。

copy_to(dest=con, cars, "cars")
df <- tbl(con, "cars")
n_limit <- 51
df %>% mutate(n=n()) %>% filter(n < n_limit) %>% collect

但是,它不适用于SQLite。要查看原因,可以检查dplyr代码生成的SQL语句:

df %>% mutate(n=n()) %>% filter(n < n_limit) %>% show_query

<SQL>
SELECT *
FROM (SELECT "speed", "dist", COUNT(*) OVER () AS "n"
FROM "cars") "rdipjouqeu"
WHERE ("n" < 51.0)

SQL包含一个窗口函数(count(*) over ()),SQLite不支持。

答案 2 :(得分:0)

因此,如果不运行查询,则无法检查结果的大小。

现在的问题是要么缓存结果服务器端并测试大小,要么简单地在R端放一些“保险”,这样我们就不会收到太多行。

在后一种情况下,简单地说:

small_t <- con %>% tbl("small_table") %>%
  filter_group_etc %>%
  head(n=5e6) %>%
  collect()

如果你得到5e6行,那么你可能溢出;我们无法将溢出与5e6行完全区分开来,但这似乎是为了在DB中获得单个执行而付出的代价?如果您真的很担心,请将5e6设置为5000001。 (而5000000L5000001L将是更好的选择,以便DB将其视为整数。)

如果您担心连接速度缓慢,这种情况不会很好,但如果您只是担心R中的内存过度流动,那么它就是一种廉价的保险,而不会给服务器带来额外的负担。

答案 3 :(得分:0)

您也可以使用 slice_sample() 函数。

collected_data <- table %>% 
  slice_sample(n = 30) %>% 
  collect()