2017-11-11 14 views
2

sqldf 쿼리를 사용자 정의 함수 내에 넣을 수있는 방법이 있습니까? 나는 이걸 겪었다 : http://r.789695.n4.nabble.com/Passing-Multiple-Variable-Into-SQLDF-Statement-as-parameters-of-function-td4636147.html, R 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) 
} 

나는 많은 데이터베이스를 가지고 있고 코드는 한 sqldf가 함수 내에서 작동하기 때문에 데이터를 추출 같은 SAS의 매크로를 작성하는 것은 매우 쉬운 것입니다. 다른 작은 프로세스를 위해 R 코드를 작성했지만 sqldf에서 훨씬 더 쉽게 사용할 수있는 복잡한 SQL 프로 시저가 많이 있습니다. 미리 감사드립니다.

+1

SQL 표현식이 올바로 구성되지 않았습니다.이 행을 사용하면 다음과 같이 처리 할 수 ​​있습니다. 'Example = paste0 ("select * from",'x ', "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"아니, 그래서 ) 그 열 당신의 extract() 함수의 매개 변수와 일치 (A data.frame으로 입력 포장과 purrr::pmap_df()를 사용하여 기준을 변경해야하는 경우 :

을 우리는이에 마지막 라인을 변경하는 경우

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

후 출력 구성 요소 이름을하지만, 그렇지 않으면 동일합니다 :

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"를 추출하고 데이터 프레임 목록을 통해 lapply 정의 할 수 있습니까? 또는 함수 추출을 사용하여 동일한 작업을 수행 할 수있는 방법이 있습니까? – Sayak

3

이 시도

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

많은 사람에게 고마움을 전한다. 정말 도움이된다. – Sayak