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

Moo*_*per 14 r dplyr r-dbi roracle dbplyr

偶尔连接到我的Oracle数据库时ROracle,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
Run Code Online (Sandbox Code Playgroud)

这可能吗?

我也对使用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
Run Code Online (Sandbox Code Playgroud)

编辑:

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

cra*_*lly 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.")
  }

}
Run Code Online (Sandbox Code Playgroud)

在没有实际运行查询的情况下,我没有看到任何方法来测试查询结果中的行数.但是,使用此方法,您始终会强制计算行号,并在数据库中首先进行计算,如果超过20,000(或任何行限制),则拒绝收集.


Til*_*ill 1

实际上,您可以通过一个 SQL 查询来实现您的目标:

使用 dplyr 的mutate而不是 summarise 将行数 (n) 作为额外列添加到数据中,然后将n < 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
Run Code Online (Sandbox Code Playgroud)

但是,它不适用于 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)
Run Code Online (Sandbox Code Playgroud)

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