2017-11-11 4 views
2

sqldfクエリをユーザ定義関数の中に入れる方法はありますか?私はこれを通りました:http://r.789695.n4.nabble.com/Passing-Multiple-Variable-Into-SQLDF-Statement-as-parameters-of-function-td4636147.htmlR call variable inside sqldfsqldfを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) 
} 

私は多くのデータベースを持っており、限りsqldfが関数内で動作としてデータを抽出するためのコードのようなSASマクロを記述するのは非常に簡単だろう。そうでなければ、いくつかの小さなプロセスのためにRコードを書いていますが、sqldfでもっと簡単になる複雑なSQLプロシージャーがたくさんあります。前もって感謝します。

+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さんのコメント

data.frame

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

のベクトルをループに使用purrr::map_df()とに結果を結合します単一のデータフレーム:

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」ではありませんので、 IE)の基準を変更する必要がある場合は、その列があなたのextract()関数のパラメータを一致data.frame(としての入力をパッケージ化してpurrr::pmap_df()を使用します。

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

この回答にはありがとうございます。ただ1つの質問、私はそれらのすべての1つで複数のデータフレームと同じ列を持っている場合、同様に抽出し、データフレームのリストを介してlapply "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

関連する問題