偶尔通过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
这可能吗?
我也对使用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
修改
见下面作为答案发布的部分解决方案,不是最佳的,因为浪费了一些时间来获取我没有用的数据。
答案 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
。 (而5000000L
或5000001L
将是更好的选择,以便DB将其视为整数。)
如果您担心连接速度缓慢,这种情况不会很好,但如果您只是担心R中的内存过度流动,那么它就是一种廉价的保险,而不会给服务器带来额外的负担。
答案 3 :(得分:0)
您也可以使用 slice_sample()
函数。
collected_data <- table %>%
slice_sample(n = 30) %>%
collect()