14

有时,当通过连接到我的Oracle数据库时ROracledbplyr我会运行一个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

4 回答 4

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(或任何行限制),则拒绝收集。

于 2017-11-07T15:47:01.440 回答
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

但是,它不适用于 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 不支持的窗口函数 ( )。

于 2018-02-02T23:36:21.377 回答
0

因此,您无法在不运行查询的情况下检查结果的大小。

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

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

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

如果你得到 5e6 行,你可能会溢出;我们无法将溢出与恰好 5e6 行区分开来,但这似乎是为了在数据库中获得单次执行而付出的小代价?如果您真的担心,请5e6设置。5000001(并且5000000L5000001L将是更好的选择,以便数据库将它们视为整数。)

如果您担心连接速度很慢,那么这不会很好,但是如果您只是担心 R 中的内存溢出,那么它是一种便宜的保险,不会给服务器带来额外的负载。

于 2018-05-28T00:22:13.223 回答
0

您也可以使用该slice_sample()功能。

collected_data <- table %>% 
  slice_sample(n = 30) %>% 
  collect()
于 2021-07-30T13:31:29.537 回答