2017-08-08 51 views
0

前幾天偶然發現了這篇文章:http://deanattali.com/blog/shiny-persistent-data-storage/#sqlite,並且想爲自己嘗試一下。使用RODBC的Shiny App中的數據存儲

但是我必須使用RODBC,這在文章中沒有提到。

目前我已經試過這樣:

table <- "[shinydatabase].[dbo].[response]" 

fieldsMandatory <- c("name", "favourite_pkg") 

labelMandatory <- function(label) { 
    tagList(
    label, 
    span("*", class = "mandatory_star") 
) 
} 

appCSS <- 
    ".mandatory_star { color: red; }" 


fieldsAll <- c("Name", "favpkg", "used_shiny", "num_years", "os_type") 

shinyApp(
    ui = fluidPage(
    shinyjs::useShinyjs(), 
    shinyjs::inlineCSS(appCSS), 
    titlePanel("Mimicking a Google Form with a Shiny app"), 

    div(
     id = "form", 

     textInput("name", labelMandatory("Name"), ""), 
     textInput("favourite_pkg", labelMandatory("Favourite R package")), 
     checkboxInput("used_shiny", "I've built a Shiny app in R before", FALSE), 
     sliderInput("r_num_years", "Number of years using R", 0, 25, 2, ticks = FALSE), 
     selectInput("os_type", "Operating system used most frequently", 
        c("", "Windows", "Mac", "Linux")), 
     actionButton("submit", "Submit", class = "btn-primary") 
    ) 

), 

    server = function(input, output, session) { 
    observe({ 
     mandatoryFilled <- 
     vapply(fieldsMandatory, 
       function(x) { 
       !is.null(input[[x]]) && input[[x]] != "" 
       }, 
       logical(1)) 
     mandatoryFilled <- all(mandatoryFilled) 
     shinyjs::toggleState(id = "submit", condition = mandatoryFilled) 

    }) 

    formData <- reactive({ 
     data <- sapply(fieldsAll, function(x) input[[x]]) 
    }) 

    saveData <- function(data) { 
     # Connect to the database 
     db<- odbcConnect(".", uid = "uid", pwd = "pwd") 
     # Construct the update query by looping over the data fields 
     query <- sprintf(
     "INSERT INTO [shinydatabase].[dbo].[response] (Name, favpkg, used_shiny, num_years, os_type) VALUES ('%s')", 
     paste(data, collapse = "', '") 
    ) 
     # Submit the update query and disconnect 
     sqlQuery(db, query) 
     odbcClose(db) 
    } 

    loadData <- function() { 
     # Connect to the database 
     odbcChannel<- odbcConnect(".", uid = "uid", pwd = "pwd") 
     # Construct the fetching query 
     query <- sprintf("SELECT * FROM [shinydatabase].[dbo].[response]") 
     # Submit the fetch query and disconnect 
     data <- sqlQuery(db, query) 
     odbcClose(db) 
     data 
    } 

    # action to take when submit button is pressed 
    observeEvent(input$submit, { 
     saveData(formData()) 
    }) 

    } 
) 

這基本上是一樣的文章和應用程序運行的,並且沒有顯示錯誤,但沒有信息被讀回我的數據庫表。

在做一個正常的INSERT INTO說法是這樣的:

sqlQuery(db, "INSERT INTO [shinydatabase].[dbo].[response] (Name, favpkg, used_shiny, num_years, os_type) VALUES ('a', 'b', 'yes', '2','mac') 

它的工作原理,所以我知道這是沒有問題的。

回答

1

我建議您將saveData函數重寫爲RODBCext。參數化查詢將幫助您澄清最終查詢的樣子,並防止SQL注入。

saveData <- function(data) { 
     # Connect to the database 
     db<- odbcConnect(".", uid = "uid", pwd = "pwd") 
     # make sure the connection is closed even if an error occurs. 
     on.exit(odbcClose(db)) 

     sqlExecute(
     channel = db, 
     query = "INSERT INTO [shinydatabase].[dbo].[response] 
       (Name, favpkg, used_shiny, num_years, os_type) 
       VALUES 
       (?, ?, ?, ?, ?)", 
     data = data 
    ) 
    } 
0

我驚訝的博客方法產生所需的結果爲R的c功能滲出到作爲查詢字符串文字,在各列中的每一個值是級聯,並且與嵌入的逗號一行字符串存儲。用隨機字母的數據表明:

sample.seed(111) 
data <- data.frame(col1 = sample(LETTERS, 5), 
        col2 = sample(LETTERS, 5), 
        col3 = sample(LETTERS, 5), 
        col4 = sample(LETTERS, 5), 
        col5 = sample(LETTERS, 5), stringsAsFactors = FALSE) 

query <- sprintf(
    "INSERT INTO [shinydatabase].[dbo].[response] (Name, favpkg, used_shiny, num_years, os_type) VALUES ('%s')", 
    paste(data, collapse = "', '") 
) 

query 
# [1] "INSERT INTO [shinydatabase].[dbo].[response] (Name, favpkg, used_shiny, 
# num_years, os_type) VALUES ('c(\"E\", \"C\", \"I\", \"U\", \"B\")', 
# 'c(\"F\", \"W\", \"R\", \"O\", \"L\")', 'c(\"Q\", \"V\", \"M\", \"T\", \"I\")', 
# 'c(\"Y\", \"V\", \"C\", \"M\", \"O\")', 'c(\"A\", \"V\", \"U\", \"I\", \"D\")')" 

然而,對於特定的需要對齊到SQL Server的話,可以考慮建築中的值設置與apply循環,然後串聯到更大的查詢字符串:

vals <- paste(apply(data, 1, function(d) paste0("('", paste(d, collapse = "', '"), "')")), collapse = ", ") 

query <- sprintf("INSERT INTO [shinydatabase].[dbo].[response] ([Name], favpkg, used_shiny, num_years, os_type) VALUES %s", vals)  
query 
# [1] "INSERT INTO [shinydatabase].[dbo].[response] (Name, favpkg, used_shiny, num_years, os_type) 
# VALUES ('E', 'F', 'Q', 'Y', 'A'), ('C', 'W', 'V', 'V', 'V'), ('I', 'R', 'M', 'C', 'U'), 
# ('U', 'O', 'T', 'M', 'I'), ('B', 'L', 'I', 'O', 'D')" 

而且,請考慮RODBC的sqlSave將整個數據幀附加到數據庫:

sqlSave(db, data, tablename = "response", append = TRUE, rownames = FALSE)