I am trying to output a table that depends on a user selecting a date range in Shiny using sqlInterpolate. Although I have succeeded outputting a table based on a selectInput (dropdown), I can't figure out how to use a dateRangeInput with sqlInterpolate.
Error:
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'AND timestamp BETWEEN '2021-12-15' AND '2021-12-17'' at line 6
My approach:
pool <- dbPool(
MariaDB(),
db = "db",
user = user,
password = password,
host = host,
port = port
)
data_pool <- pool %>% tbl("table")
ui <- fluidPage(
uiOutput("daterange"),
tableOutput("table")
)
server <- function(input, output, session) {
output$daterange <- renderUI({
dateRangeInput("daterange2", "Date:", start = "2021-12-15", end = "2021-12-17")
})
data <- reactive({
req(input$daterange2[1], input$daterange2[2])
sql2 <- "
SELECT
STR_TO_DATE(timestamp, '%Y-%m-%d') AS timestamp
FROM table
WHERE timestamp BETWEEN ?date1 AND ?date2
"
query <- sqlInterpolate(pool, sql2, date1 = input$daterange[1], date2 = input$daterange[2])
dbGetQuery(pool, query)
})
output$table1 <- renderTable({
data()
})
}
Related
I am building an app using RShiny which queries a SQLite database.
I have finished writing the code for the app, but I am having trouble reducing the complexity of the query / using glue_sql function. As it is right now there are many OR statements which I believe might be slowing the query down.
I used sqlInterpolate to conduct the query to prevent any SQL injections, as this is what is cited on the RShiny website as what should be done.
This is my code as of right now:
UI:
ui <- fluidPage(theme = shinytheme("cerulean"),
titlePanel("Application"),
sidebarLayout(
sidebarPanel(
checkboxGroupInput(inputId = "Assay", label = "Assay Type:", choices = c("MBP1","EV","Oxidation","WGAS","WLS","Genomics"),
selected = c("MBP1","EV","Oxidation","WGAS","WLS","Genomics")), #Assay
checkboxGroupInput(inputId = "Vendor", label = "Vendor:", choices = c("AKE","BSA"),
selected = c("AKE","BSA")), #Vendor
actionButton("read", "Read From Database"),
checkboxGroupInput(inputId = "Study",
label = "Study Type",
choices = c("MP", "CSP"),
selected = c("MP", "CSP")),
dateRangeInput(inputId = "Date", label = "Sample Date Range:", format ="yyyy-mm-dd"), #Date
actionButton("read", "Read From Database")),
mainPanel(h1("Sample Count:"),
dataTableOutput("Samples_Sorted_by_Study"),
dataTableOutput("Samples_Sorted_by_Assay_and_Vendor")) #table
) #closing navbarPage
) #closing fluidPage UI
Server:
server <- function(input, output){
#Storing values in myData variable
myData <- reactiveValues()
observeEvent(
input$read,
{
myData$assay <- input$Assay
myData$vendor <- input$Vendor
myData$date_val <- input$Date
myData$studytype <- input$Study
#Opening database connection
connectiontodb <- dbConnect(RSQLite::SQLite(), "example.sqlite")
#Shortening Query??
#myData$study_int <- c(myData$studytype[1], myData$studytype[2])
#myData$study_int <- glue_sql("{myData$study_int*}")
#conducting first query of samples grouped by study
#Sample query of example.sqlite database
myData$interpolatequery1 <- sqlInterpolate(connectiontodb,
"Select study, count(genomic_id), count(specimen_id)
FROM exampletable WHERE study = ?studytype1 OR study = ?studytype2
GROUP BY study",
studytype1 = myData$studytype[1], studytype2 = myData$studytype[2])
myData$exampledataSQLQuery1 <- dbGetQuery(connectiontodb, myData$interpolatequery1)
#Closing connection to database
dbDisconnect(connectiontodb)
#Correcting Column Names
colnames(myData$exampledataSQLQuery1) <- c("Study", "Genomic Id", "Specimen Id")
}
)
output$Samples_Sorted_by_Study <- renderDataTable(myData$exampleSQLQuery1)
}
shinyApp(ui = ui, server = server)
How can I condense the query so that there isn't a need for so many OR statements, but also protect against SQL injection?
I have the following code
res<-function(y) {
data <- sqlQuery(dbhandle, "select count([respondent serial]) from data1
where [variable]= y")
print(data)
}
dbhandle is the connection established to sql server.
Now if i pass res(gender) am not getting results as i would normally get with a select statement in SQL.
Any help with this is appreciated.
This happens because the string inside the query does not understand that y is a variable. Hence paste0() is here to help:
res<-function(y) {
data <- sqlQuery(dbhandle, paste0("select count([respondent serial]) from data1
where [variable]=", y))
return(data)
}
You can call the function by using res("gender").
EDIT
In order to pass a numeric value, you can do:
res2<-function(y) {
data <- sqlQuery(dbhandle, paste0("select count([respondent serial]) from data1
where [week number] =", y))
return(data)
}
You can check this out as well: res2(201752)
I am working on a project to update a SQL database with a Shiny app using D3 Table Filter.
I am able to query the server with different text inputs and the table will render with only those rows. The next step is to edit the table in the shiny app, and have that send a query back to the server to update it.
I have enabled editing in specific columns. How could I make an edit and have it send a query?
Thank you very much in advance.
Here is my code so far:
#install.packages("devtools")
#devtools::install_github("ThomasSiegmund/D3TableFilter")
library(shiny)
library(htmlwidgets)
library(D3TableFilter)
library(RSQLite)
library(RODBCext)
library(sqldf)
dbhandle = odbcDriverConnect(connection = "driver={SQL Server};server= ... ;database= ... ;trusted_connection=true")
fulldata = sqlExecute(dbhandle, "SELECT * FROM ...", fetch = TRUE, stringsAsFactors = FALSE)
ui <- fluidPage(
# Application title
titlePanel("Patient Search"),
sidebarLayout(
sidebarPanel(
textInput(inputId = "Id", label = "Search by Account Number, Date of Birth (YYYY-MM-DD) or Last Name"),
textInput(inputId = "NextAppt", label = "Search by Next Appointment (YYYY-MM-DD)"),
submitButton(text = "Go!")
),
mainPanel(
title = 'Patient Search with D3 Table Filter in Shiny',
fluidRow(
column(width = 12, d3tfOutput('data'))
)
)
)
)
# server.R
# --------------------------------------------------------
server <- shinyServer(function(input, output, session) {
#this reactive will return the row numbers that will need to be returned in our table.
#this could depend on any of our inputs: last name, DoB, account number, or next appointment
search.criteria <- reactive({
out <- c()
outAppt <- c()
if(grepl("\\d{4}\\-\\d{2}\\-\\d{2}", input$Id)==TRUE){
out <- which(fulldata$PatientDOB==input$Id)
print(out)
} else if(grepl("\\d{5}", input$Id)==TRUE){
out <- which(fulldata$AccountNo==input$Id)
} else{
out <- which(fulldata$PatientLastName==toupper(input$Id))
}
# filter for appointment
if(grepl("\\d{4}\\-\\d{2}\\-\\d{2}", input$NextAppt)==TRUE){
outAppt <- which(fulldata$NextAppt==input$NextAppt)
if(length(out)){
out <- intersect(out, outAppt)
}else{
out <- outAppt
}
}
out
})
#make the output table
output$data <- renderD3tf({
# Define table properties
tableProps <- list(
btn_reset = TRUE,
# alphabetic sorting for the row names column, numeric for all other columns
col_types = c("string", rep("number", ncol(fulldata)))
);
d3tf(fulldata[search.criteria(),],
tableProps = tableProps,
extensions = list(
list(name = "sort")
),
showRowNames = TRUE,
tableStyle = "table table-bordered",
#this optional argument enables editing on these specific columns
edit = c("col_49", "col_50", "col_51", "col_52", "col_53"));
})
#NEED TO ADD SOMETHING HERE TO SEND QUERY TO SERVER WHEN USER EDITS
})
runApp(list(ui=ui,server=server))
I used rhandsontable. It works better as you can convert the output using hot_to_r. But because of it's simple excel like formatting, it's difficult to render images like DT
If only data, go ahead and use rhandsontable.
Eg.
rhandsontable(df) %>%
hot_cols(colWidths = c(80,150,80,80,80,80,200,200,80,80,300,80,80), manualColumnResize = TRUE) %>%
hot_col(2:13, renderer = "html") %>%
hot_col(2:13, renderer = htmlwidgets::JS("safeHtmlRenderer")) %>%
hot_col(1, renderer = "
function(instance, td, row, col, prop, value, cellProperties) {
var escaped = Handsontable.helper.stringify(value),
img;
if (escaped.indexOf('http') === 0) {
img = document.createElement('IMG');
img.src = value;
Handsontable.dom.addEvent(img, 'mousedown', function (e){
e.preventDefault(); // prevent selection quirk
});
Handsontable.dom.empty(td);
td.appendChild(img);
}
else {
// render as text
Handsontable.renderers.TextRenderer.apply(this, arguments);
}
return td;
}")
})
observeEvent(input$submitComments, {
a = hot_to_r(input$upcomingAuctionsTable)
# sqlSave(myConnUpcom, a, tablename = "test", rownames = FALSE, varTypes = c(date = "varchar(255)"))
sqlUpdate(myConnUpcom, a, tablename = "temp", index = "item_id")
})
I do not understand the error which appears in my SQL Query. If i choose in SQL
Select * -> it is working fine and i do get the table,
however if i select any of the column/s it is giving me an Error:
Error in $<-.data.frame(*tmp*, "PROBE", value =
structure(integer(0), .Label = character(0), class = "factor")) :
replacement has 0 rows, data has 1427
Here is my SQL code:
if(input$filter == 1){
sqlOutput <- reactive({
sqlInput <- paste("select * from DWH.PROBE where DWH.PROBE.Nr =",paste0("'",d(),"'"), "And DWH.PROBE.AB BETWEEN",input$abmfrom, "AND",input$abmto,"ORDER BY Datum asc")
print(sqlInput)
dbGetQuery(con$cc, sqlInput)
})
}else{
sqlOutput <- reactive({
sqlInput <- paste("select * from DWH.PROBE where DWH.PROBE.S BETWEEN",d2(), "AND",input$sgehalt2, "And DWH.PROBE.AB BETWEEN",input$abmfrom2, "AND",input$abmto2,"ORDER BY Datum asc")
dbGetQuery(con$cc, sqlInput)
})}
And if i just add to those SQL Queries
select DWH.PROBE.S, DWH.PROBE.AB.. from DWH.PROBE
Then it comes above mentioned Error.
Additionally i need to say if i will use this SQL Query in a simple code:
rs <- dbSendQuery(con, paste("select DWH.PROBE.AB, DWH.PROBE.S from DWH.PROBE where DWH.PROBE.Nr = '50' And DWH.PROBE.AB BETWEEN 40 AND 50 ORDER BY Datum asc"))
data <- fetch(rs)
It is giving me the results...
Any ideas?
[EDIT *as my question is not a duplicate]
The question posted here: http://stackoverflow.com/questions/32048072/how-to-pass-input-variable-to-sql-statement-in-r-shiny actually has nothing to do with my topic. As we can see the error in this post:
Error in .getReactiveEnvironment()$currentContext() : Operation not
allowed without an active reactive context. (You tried to do something
that can only be done from inside a reactive expression or observer.)
I do not have a problems with passing input variable to sql statement and additionally if you can see in my SQL: The Query is in reactive context!:
sqlOutput <- reactive({...
The solution for above question was:
to put SQL Query in reactive context which is not a thing in my case
[EDIT 2] -> bits related to sqlOutput()
Here is a bit of code related to sqlOutput() which i am using in my Shiny App (at the moment this is the only bit because i am stuck with SQL Query)
output$tabelle <- DT::renderDataTable({
data <- sqlOutput()
data$PROBE <- as.factor(as.character(data$PROBE))
data
}, rownames=TRUE, filter="top", class = 'cell-border stripe',
options = list(pageLength = 100, lengthMenu=c(100,200,500), columnDefs = list(list(width = '200px', targets = "_all"),list(bSortable = FALSE, targets = "_all"))))
Thanks
Error doesn't relate to SQL statements, however, try changing your code to below:
sqlOutput <- reactive({
if(input$filter == 1){
sqlInput <- paste("select * from DWH.PROBE where DWH.PROBE.Nr =",paste0("'",d(),"'"), "And DWH.PROBE.AB BETWEEN",input$abmfrom, "AND",input$abmto,"ORDER BY Datum asc")
} else {
sqlInput <- paste("select * from DWH.PROBE where DWH.PROBE.S BETWEEN",d2(), "AND",input$sgehalt2, "And DWH.PROBE.AB BETWEEN",input$abmfrom2, "AND",input$abmto2,"ORDER BY Datum asc")
}
dbGetQuery(con$cc, sqlInput)
})
Alright, I modified the script following #Pork Chop advice:
server.R
library(shiny)
library(DT)
library(RMySQL)
con <- dbConnect(MySQL(), user="myuser", host="myhost", dbname="mydb")
shinyServer(function(input, output) {
sqlOutput <- reactive({
sqlInput <- paste0("select * from mydb.mytable",
" where value < ", input$value,
";")
dbGetQuery(con, sqlInput)
})
output$table <- DT::renderDataTable(sqlOutput(), server=TRUE, rownames=FALSE, filter="top", options=list(pageLength=10))
output$download <- downloadHandler("filtered.data.txt", content = function(file) {
rows <- input$table_rows_all
write.table(sqlOutput()[rows, ], file, sep="\t", quote=FALSE, col.names=TRUE, row.names=FALSE)
})
})
The DataTable now works!
However when I try to download the displayed data, I get a file with only column names and no data. According to the DT docs, input$table_rows_all should contain the row indices of the displayed table.
What's wrong?
I'm having troubles with Shiny reactivity and a MySQL database.
In short, I get an input value from the user, create an SQL query, capture the output and display it as a DataTable.
The output can be further filtered using the DataTable column filters and the user should be able to download the filtered dataset.
server.R
library(shiny)
library(DT)
library(RMySQL)
con <- dbConnect(MySQL(), user="myuser", host="myhost", dbname="mydb")
shinyServer(function(input, output) {
sqlInput <- reactive({
paste0("select * from mydb.mytable",
" where value < ", input$value,
";")
})
sqlOutput <- reactive({
dbGetQuery(con, sqlInput)
})
output$table <- DT::renderDataTable(sqlOutput, server=TRUE, rownames=FALSE, filter="top", options=list(pageLength=10))
output$download <- downloadHandler("filtered.data.txt", content = function(file) {
rows <- input$table_rows_all
write.table(sqlOutput[rows, ], file)
})
})
Instead of the DataTable, I get this error:
This works as expected if I embed sqlInput and sqlOutput within a reactive expression in DT::renderDataTable(), but then I'm not able to refer to sqlOutput from within downloadHandler() (object 'sqlOutput' not found). I thought this was the perfect use case for using reactive() but I can't get it to work.
What's the best way to make this work?
Any help is much appreciated, thanks!
1. sqlOutput is a function so change it to sqlOutput()
2. Try this, note this will export is as .csv hope its ok
output$download <- downloadHandler(filename = function() {paste(Sys.time(), ' Fltered_data.csv', sep='')}, content = function(file) {write.csv(sqlOutput()[input$table_rows_all, ], file, row.names = FALSE)})