【问题标题】:Putting sqldf inside r function将 sqldf 放入 r 函数中
【发布时间】:2017-11-11 18:44:47
【问题描述】:

有没有办法将 sqldf 查询放入用户定义的函数中?我经历过这个:http://r.789695.n4.nabble.com/Passing-Multiple-Variable-Into-SQLDF-Statement-as-parameters-of-function-td4636147.htmlR call variable inside sqldf

我的示例代码是这样的:

db1 = data.frame(a = c(1,2,3), b = c("a","b","c"))
db2 = data.frame(a = c(1,2,3), b = c("b","a","c"))
db = list(db1,db2)

extrct = function(x){
Example=paste0("select * from", x , "where b
='","b", "'")
sqldf(Example,verbose=TRUE) 
}

我有很多数据库,只要 sqldf 在函数中工作,编写 SAS 宏之类的代码来提取数据就很容易。否则我已经为一些小进程编写了 R 代码,但是有很多复杂的 SQL 过程在 sqldf 中会容易得多。提前致谢。

【问题讨论】:

  • 您的 SQL 表达式格式不正确。使用这条线,你会很高兴:Example=paste0("select * from ", 'x' , " where b='","b", "'", collapse="")。请记住测试函数的内部部分,以确保它们产生预期的输出。

标签: r function sqldf


【解决方案1】:

试试这个:

library(sqldf)

extract <- function(x, envir = parent.frame(), verbose = TRUE, ...) {
  fn$sqldf("select * from [$x] where b = 'b'", envir = envir, verbose = verbose, ...)
}

# sample runs
extract("db1")
extract("db2")

Map(extract, c("db1", "db2"))

db <- setNames(db, c("db1", "db2"))
lapply(names(db), extract, envir = list2env(db))

如果我们将最后一行更改为此,则输出将具有组件名称,但其他方面相同:

sapply(names(db), extract, envir = list2env(db), simplify = FALSE)

【讨论】:

    【解决方案2】:

    我就是这样写的。 sqldf 更自然地处理字符串,而不是使用nse。因此,只需传入要使用源的 data.frame 的字符串/名称即可。

    library(sqldf); requireNamespace("checkmate")
    db1 <- data.frame(a = c(1,2,3), d = c("a","b","c"), stringsAsFactors = F)
    
    extract <- function( table_name, criteria_d ) {
      checkmate::assert_character(table_name, min.chars=1, len=1, any.missing=F)
      checkmate::assert_character(criteria_d, min.chars=1, len=1, any.missing=F)
    
      # Half-way attempt to prevent sql-injection. Values would need to be only numbers, letters, and underscores.
      checkmate::assert_character(table_name, pattern="^\\w+$", len=1, any.missing=F)
      checkmate::assert_character(criteria_d, pattern="^\\w+$", len=1, any.missing=F)
    
      sql <- paste0("select * from [", table_name , "] where d ='", criteria_d, "'")
    
      cat("Executing: `", sql, "`\n", sep="")
      sqldf(sql, verbose=F) 
    }
    
    extract("db1", "b")
    

    如果由于某种原因您不知道变量的字符串/名称,则等效:extract(quote(db1), "b")

    一些笔记。

    1. 我将变量的名称更改为“d”以使事情更清楚。
    2. 我认为 db2db 与您的场景无关。
    3. 我尽量不要过多地更改您的代码。如果此函数曾经连接到真实数据库,请防范sql injection
    4. 如果您的 sql 变得有点复杂,请考虑使用glue::glue_sql()

    编辑以回应@Sayak 的评论

    使用purrr::map_df() 循环遍历data.frame的向量names

    c("db1", "db2") %>% 
      purrr::map_df(extract, "b") 
    

    并将结果合并到一个单独的data.frame中:

    Executing: `select * from [db1] where d ='b'`
    Executing: `select * from [db2] where d ='b'`
      a d
    1 2 b
    2 1 b
    

    这非常巧妙,不需要后续调用dplyr::bind_rows()

    如果您需要更改标准(ie,因此它并不总是“b”),请使用 purrr::pmap_df() 将输入打包为 data.frame(其列与您的参数匹配extract()函数:

    ds_input <- tibble::tribble(
      ~table_name, ~criteria_d,
      "db1",         "b",
      "db1",         "c",
      "db2",         "c"
    )
    
    ds_input %>% 
      purrr::pmap_df(extract)
    
    # Executing: `select * from [db1] where d ='b'`
    # Executing: `select * from [db1] where d ='c'`
    # Executing: `select * from [db2] where d ='c'`
    #   a d
    # 1 2 b
    # 2 3 c
    # 3 3 c
    

    【讨论】:

    • 非常感谢您的回答。请只问一个问题,如果我有多个数据框并且每个数据框都具有相同的列,是否可以类似地定义提取“b”并通过数据框列表进行应用?或者有什么方法可以对函数提取做同样的事情?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-03
    • 2015-11-07
    • 2015-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多