在 dplyr 中,sql 构建器如何工作?

In dplyr how does the sql builder work?

在R中,我们可以有如下表达式:

tbl(con, "table1") %>% filter(col1 > 12)

执行

select * from table1 where col1 > 12

但是如果你有 tbl(con, "table1"),它会从 table.

执行 select *

第一个函数 tbl(con, "table1") 如何知道它有附加函数链接到它,并且需要等待链结束才能构建正确的 sql 查询并执行命令。是的,我知道它使用惰性求值,但我无法编写一个简单的玩具示例来以相同的方式构建字符串

shoppingList("I need to get")

打印出来"I need to get nothing"

shoppingList("I need to get") %>% item("apples") %>% item("oranges")

打印出来"I need to get apples and oranges"

也许让您感到困惑的是 dplyr 函数 tblfilter 实际上并没有将任何代码发送到数据库以供执行。当你 运行

tbl(con, "table1") %>% filter(col1 > 12)

返回的是一个包含 sql 查询的 tbl_dbi 对象。当您在 R 中以交互方式 运行 这行代码时,返回的 tbl_dbi 对象将传递给 print 函数。为了打印 tbl_dbi ,必须在数据库中执行查询。您可以通过将输出保存到变量来查看。

q <- tbl(con, "table1") %>% filter(col1 > 12)
class(q)

以上两行没有向数据库发送任何内容。 tbl 函数返回了一个 tbl_dbi 对象,过滤器修改了那个 tbl_dbi 对象。最后将结果保存到变量q中。 当我们打印 q 然后 SQL 被发送到数据库。因此 tbl 函数不需要知道在它之后调用的任何其他 dplyr 函数(如本例中的 filter )。无论如何,它的行为都是一样的。它总是 returns 一个 tbl_dbi 对象。

现在 dbplyr 如何从更简单的查询构建更复杂的查询超出了我的范围。

这是一些实现您的示例的代码。

library(dplyr)

shoppingList <- function(x){
     stopifnot(is.character(x))
     class(x) <- c("first", "shoppingList", class(x))
     x
}

item <- function(x, y){
     if("first" %in% class(x)){
          out <- paste(x, y)
     } else {
          out <- paste0(x, " and ", y)
     }
     class(out) <- c("shoppingList", class(out))
     out
}

print.shoppingList <- function(x){
     # code that only runs when we print an object of class shoppingList
     if("first" %in% class(x)) x <- paste(x, "nothing")
     print(paste0("***", x, "***"))
}

shoppingList("I need to get") 
#> [1] "***I need to get nothing***"

shoppingList("I need to get") %>% item("apples") %>% item("oranges")
#> [1] "***I need to get apples and oranges***"

但是print怎么知道发送SQL到数据库呢?我的(过于简单化的)概念性答案是 print 是一个通用函数,其行为会根据传入对象的 class 而有所不同。实际上有很多 print 函数。在上面的示例中,我为 class shoppingList 的对象创建了一个特殊的打印函数。您可以想象一个特殊的 print.tbl_dbi 函数,它知道如何处理 tbl_dbi 对象,方法是将它们包含的查询发送到它们连接的数据库,然后打印结果。我认为实际的实现更复杂,但希望这能提供一些直觉。