根据 R Shiny App 中的用户输入创建动态 SQL 查询
Create dynamic SQL query depending on user input in R Shiny App
我有一个闪亮的应用程序,用户可以在其中过滤 SQL 电影数据库。到目前为止,您只能按不同的国家筛选。
con <- dbConnect(RSQLite::SQLite(), 'Movies.db')
movies_data <- dbReadTable(con, 'Movies')
ui <- fluidPage(
fluidRow(
selectInput(
inputId = "country",
label = "Country:",
choices = movies_data$journal,
multi=T
),
br(),
fluidRow(width="100%",
dataTableOutput("table")
)
)
)
server <- function(input, output, session) {
output$table <- renderDataTable({
dbGetQuery(
conn = con,
statement = 'SELECT * FROM movies WHERE country IN ( ? )',
params = list(input$country))
})
}
shinyApp(ui = ui, server = server)
现在我想给用户更多的过滤器,例如演员或流派。所有过滤器都是多选和可选的。我怎样才能创建声明动态?我会为每个可能的组合使用一些 switch 语句吗(即没有国家过滤器,只有动作片)?这对我来说似乎有点累。
首先,您说过滤器是可选的,但我看不到在您的代码中禁用它的方法。我假设取消选择所有选项是您禁用过滤器的方式,或者至少它打算以这种方式工作。如果为任何过滤器选择了所有选项,那么当前的方法应该可以正常工作,并且只会显示所有电影。
您或许可以逐个构建整个查询,然后在最后将它们全部粘贴在一起。
基本查询:'SELECT * FROM movies'
国家过滤器:'country in '输入国家
演员过滤器:'actor in' 输入演员
流派过滤器:'genre in' 输入流派
然后你把它们全部粘贴在一起。
总结:基本查询。然后,如果任何过滤器处于活动状态,请添加 WHERE。将所有过滤器连接在一起,用 AND 分隔。将最终查询作为直接字符串传递。
您甚至可以将过滤器放入列表中以便于解析。
# Here, filterList is a list containing input$country, input$actor, input$genre
# and filterNames contains the corresponding names in the database
# e.g. filterList <- list("c1", list("a1", "a2"), "g1")
# filterNames <- filterNames <- list("c", "a", "g")
baseQuery <- "SELECT * FROM movies"
# If any of the filters have greater than 0 value, this knows to do the filters
filterCheck <- any(sapply(filterList, length)>0)
# NOTE: If you have a different selection available for None
# just modify the sapply function accordingly
if(filterCheck)
{
baseQuery <- paste(baseQuery, "WHERE")
# This collapses multiselects for a filter into a single string with a comma separator
filterList <- sapply(filterList, paste, collapse = ", ")
# Now you construct the filters
filterList <- sapply(1:length(filterList), function(x)
paste0(filterNames[x], " IN (", filterList[x], ")"))
# Paste the filters together
filterList <- paste(filterList, collapse = " and ")
baseQuery <- paste(baseQuery, filterList)
}
# Final output using the sample input above:
# "SELECT * FROM movies WHERE c IN (c1) and a IN (a1, a2) and g IN (g1)"
现在使用baseQuery作为直接查询语句
我有一个闪亮的应用程序,用户可以在其中过滤 SQL 电影数据库。到目前为止,您只能按不同的国家筛选。
con <- dbConnect(RSQLite::SQLite(), 'Movies.db')
movies_data <- dbReadTable(con, 'Movies')
ui <- fluidPage(
fluidRow(
selectInput(
inputId = "country",
label = "Country:",
choices = movies_data$journal,
multi=T
),
br(),
fluidRow(width="100%",
dataTableOutput("table")
)
)
)
server <- function(input, output, session) {
output$table <- renderDataTable({
dbGetQuery(
conn = con,
statement = 'SELECT * FROM movies WHERE country IN ( ? )',
params = list(input$country))
})
}
shinyApp(ui = ui, server = server)
现在我想给用户更多的过滤器,例如演员或流派。所有过滤器都是多选和可选的。我怎样才能创建声明动态?我会为每个可能的组合使用一些 switch 语句吗(即没有国家过滤器,只有动作片)?这对我来说似乎有点累。
首先,您说过滤器是可选的,但我看不到在您的代码中禁用它的方法。我假设取消选择所有选项是您禁用过滤器的方式,或者至少它打算以这种方式工作。如果为任何过滤器选择了所有选项,那么当前的方法应该可以正常工作,并且只会显示所有电影。
您或许可以逐个构建整个查询,然后在最后将它们全部粘贴在一起。
基本查询:'SELECT * FROM movies'
国家过滤器:'country in '输入国家
演员过滤器:'actor in' 输入演员
流派过滤器:'genre in' 输入流派
然后你把它们全部粘贴在一起。
总结:基本查询。然后,如果任何过滤器处于活动状态,请添加 WHERE。将所有过滤器连接在一起,用 AND 分隔。将最终查询作为直接字符串传递。
您甚至可以将过滤器放入列表中以便于解析。
# Here, filterList is a list containing input$country, input$actor, input$genre
# and filterNames contains the corresponding names in the database
# e.g. filterList <- list("c1", list("a1", "a2"), "g1")
# filterNames <- filterNames <- list("c", "a", "g")
baseQuery <- "SELECT * FROM movies"
# If any of the filters have greater than 0 value, this knows to do the filters
filterCheck <- any(sapply(filterList, length)>0)
# NOTE: If you have a different selection available for None
# just modify the sapply function accordingly
if(filterCheck)
{
baseQuery <- paste(baseQuery, "WHERE")
# This collapses multiselects for a filter into a single string with a comma separator
filterList <- sapply(filterList, paste, collapse = ", ")
# Now you construct the filters
filterList <- sapply(1:length(filterList), function(x)
paste0(filterNames[x], " IN (", filterList[x], ")"))
# Paste the filters together
filterList <- paste(filterList, collapse = " and ")
baseQuery <- paste(baseQuery, filterList)
}
# Final output using the sample input above:
# "SELECT * FROM movies WHERE c IN (c1) and a IN (a1, a2) and g IN (g1)"
现在使用baseQuery作为直接查询语句