dbplyr filter() 和惰性求值

dbplyr filter() and lazy evaluation

我有一个很大的 MySQL table(92 列,300 万行),我在 R 中与 dbplyr 争论不休。我是这个包的新手,对 dbplyr 的惰性评估有一个或两个问题,因为我遇到了一些相当缓慢的问题。

假设我已连接到我的数据库并希望执行 select 操作以减少列数:

results<- my_conn_table %>%
            select(id, type, date)

尽管有数百万行,但查看 results 几乎是瞬时的(为清楚起见只显示 3 行):

> results

# Source:   lazy query [?? x 3]
# Database: mysql []
      id type     date   
   <int> <chr>    <date>    
 1     1 Customer 1997-01-04 
 2     2 Customer 1997-02-08 
 3     3 Business 1997-03-29 
 ...
 

但是,如果我尝试执行过滤操作:

results<- my_df %>%
            select(id, type, date) %>%
            filter(type == "Business")

该操作需要非常 的处理时间(在某些情况下需要数十分钟)。这么长的处理时间是 nrow ~= 300 万的简单函数吗? (换句话说,我对此无能为力,因为数据集很大。)或者有人可以提出一些通用方法来提高性能吗?

我最初对惰性评估的理解是 filter() 查询只会 return 前几行,以防止长时间 运行 的情况。如果我想要所有数据,我可以 运行 collect() 将结果收集到我的本地 R 会话中(我 预计会花费大量时间,具体取决于在查询中。)

您可以尝试的一件事是 运行 相同的查询,但使用 DBI 包:

res <- DBI::dbSendQuery(con, "SELECT id,type,date FROM your_table_name WHERE type = 'business'")

如果速度一样,就是你的数据。如果不是,则为 dbplyr。您也可以尝试使用不同的过滤器参数(可以指定一个 ID 或日期,看看需要多长时间),看看问题是否仍然存在。

基于@NovaEthos 的回答,您可以调用 show_query(results) 来获取 dbplyr 生成并传递给数据库的 SQL 查询。在此处发布此查询将清楚查询数据库的方式是否效率低下。

要调查的另一件事是数据的索引方式。就像书中的索引一样,数据库中的索引 table 有助于更快地查找记录。

您可能只需要 type = 'business' 的 1000 条记录,但如果这些记录仅从第 200 万行开始,那么计算机必须扫描三分之二的数据才能找到第一条感兴趣的记录.

您可以使用如下方式向现有数据库 table 添加索引:

query <- paste0("CREATE NONCLUSTERED INDEX my_index_name ON", database_name, ".", schema_name, ".", table_name, "(", column_name, ");")

DBI::dbExecute(db_connection, as.character(query))

请注意,此语法适用于 SQL 服务器。您的数据库可能需要稍微不同的语法。在实践中,我将其包装在一个带有额外检查的函数中,例如 'does the indexing column exist in the table?' 这已被证明 here.