2017-11-11 66 views
2

有沒有什麼辦法可以把sqldf查詢放到用戶定義的函數中?我已經通過了這個:http://r.789695.n4.nabble.com/Passing-Multiple-Variable-Into-SQLDF-Statement-as-parameters-of-function-td4636147.htmlR call variable inside sqldf把sqldf放在r函數裏

我的示例代碼會是這樣的:

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) 
} 

我有很多的數據庫,這將是很容易編寫SAS宏象代碼,只要sqldf工作的函數中提取數據。否則,我已經爲一些小的進程編寫了R代碼,但是有很多複雜的SQL程序在sqldf中會更容易。提前致謝。

+1

您的SQL表達式格式不正確。使用這一行,你會很好的去:'Example = paste0(「select * from」,'x',「where b ='」,「b」,「'」,collapse =「」)''。請記住測試函數的內部部分以確保它們產生預期的輸出。 – lmo

回答

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

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

的載體和結合的結果成單個數據幀:

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()

如果需要改變標準(即,所以它並不總是「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 
+0

非常感謝這個答案。如果我在每個數據框中都有多個數據框和相同的列,請問只有一個問題,如果在數據框列表中定義了類似的提取「b」的提取方法?或者有什麼辦法可以對函數提取做同樣的事情嗎? – Sayak

3

試試這個:

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) 
+0

非常感謝,這真的非常有幫助。 – Sayak