【问题标题】:In dplyr how does the sql builder work?在 dplyr 中,sql builder 是如何工作的?
【发布时间】:2018-05-25 18:36:01
【问题描述】:

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

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

执行

select * from table1 where col1 > 12

但是如果你有 tbl(con, "table1"),它会执行 select * from table。

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

shoppingList("I need to get")

打印出“我什么都不需要”

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

打印出“我需要买苹果和橘子”

【问题讨论】:

    标签: r dplyr dbplyr


    【解决方案1】:

    也许让您感到困惑的是 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)。无论如何,它的行为都是一样的。它总是返回一个 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 是一个通用函数,其行为会根据传入的对象类而有所不同。实际上有很多 print 函数。在上面的示例中,我为 shoppingList 类的对象创建了一个特殊的打印函数。您可以想象一个特殊的print.tbl_dbi 函数,它知道如何处理 tbl_dbi 对象,方法是将它们包含的查询发送到它们连接的数据库,然后打印结果。我认为实际的实现更复杂,但希望这能提供一些直觉。

    【讨论】:

    • SQL查询构建部分我能理解。但是在文档中,他们说在对对象进行操作之前,他们不会执行 sql。来自该站点的引用:“”“直到您请求数据(例如,通过打印 tailnum_delay),dplyr 才会生成 SQL 并从数据库中请求结果。”“”
    • 完全正确。因此,要回答您的问题,我会说 tbl 不需要了解链接到它的函数的任何信息,因为它不会在数据库中执行任何操作。它生成一个 SQL 查询/tbl_dpi 对象,其他链接函数修改该查询/tbl_dbi。
    • 我的问题是,如果你说 print(tbl),它知道执行 sql 语句。怎么样?
    • 我想总的来说我想通了。它使用 R6 类,并覆盖了 print 函数,该函数确实对 SQL 表进行了一次小调用
    • 回复:Now how dbplyr builds up more complex queries from simpler ones is beyond me.。它基本上只是嵌套选择查询。如果您查看对象的结构,您会发现这些,对 group_by 稍加改动。如果有的话,并没有做太多的优化,只是一步一步地翻译成sql和嵌套查询,但我猜有些DBMS可以优化服务器端的嵌套查询。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-21
    • 1970-01-01
    • 2016-01-28
    • 2014-10-23
    • 1970-01-01
    • 2021-01-11
    • 2011-05-01
    相关资源
    最近更新 更多