如何将SQL数据库实时保存到Dataframe中?Shiny

3
1. 问题: 我正在使用一个SQL数据库,它既被我的闪亮应用程序更新,也被另一个闪亮应用程序更新。我使用df=isolate(reactivePoll ())来对数据框进行子集分析,因为reactivePoll会生成一个函数作为结果,然而isolate()方法存在一个严重的问题:只有当闪亮应用程序打开时才会更新数据框,它只运行一次。

如何实时更新数据框对象,并能够对其进行子集分析,而不出现错误:Error in: object of type 'closure' is not subsettableWarning: 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.)

在渲染函数中,reactivepoll函数非常适合实时显示表格,但是如何实现数据框的实时更新以及对其进行子集分析(例如 df$col1 df$col2 等)?

server(input,output,session){
df=reactivePoll(intervalMillis = 2000,session, checkFunc = function(){
   
    QUERY1= "SELECT *  FROM table"
       df_rs = dbSendQuery(storiesDb,QUERY1)
    dbFetch(df_rs)}, 
    valueFunc =function (){
     
      QUERY1= "SELECT *  FROM table"
    
      df_rs = dbSendQuery(storiesDb,QUERY1)
      dbFetch(df_rs)
      
      
      } ) 
dataframe=  isolate(df()) # this approach makes the dataframe update only when the shiny app open.
}

尝试

1-我已经尝试使用dataframe=reactive({df ()}),但在尝试对dataframe$col1进行子集分配时出现错误:Error in: object of type 'closure' is not subsettable

2-我已经尝试使用dataframe=df (), 但是出现了错误: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.)


1
错误和您的代码表明,您正在尝试不正确地访问 df()。它 必须reactiveeventReactiveobserveobserveEvent 或其中一个 render* 函数内部。这对于任何反应式表达式都是正确的,而不仅仅是从 reactivePoll 返回的表达式。 - r2evans
谢谢您的回答,我尝试了这个方法,但是出现了以下错误: 警告:Error in: object of type 'closure' is not subsettable 53: ifelse [# 267] 52: server [# 267] 错误:object of type 'closure' is not subsettable - KmnsE2
1
不,我没有忘记 () - KmnsE2
两者的想法是相同的:当表格发生任何变化时,将其全部发送给我。 - KmnsE2
两个答案中有一个对您有帮助,KmnsE2吗? - r2evans
显示剩余5条评论
2个回答

2
我认为你最好使用invalidateLater而不是reactivePoll。后者旨在使用一个相对廉价的“检查”函数和一个更昂贵的值检索函数。
在你的情况下,你打算每2秒运行一次查询,无论如何都要运行。没有“检查”需要。
此外,shiny中的reactive*函数倾向于是惰性的(如果没有任何依赖项,它将永远不会触发),而observe*函数则更加贪婪(并且不考虑依赖关系而触发)。
这个怎么样?
dat <- reactiveValue(NULL)
observe({
  invalidateLater(2000)
  QUERY1 = "SELECT * FROM table"
  ret <- dbGetQuery(storiesDb, QUERY1)
  dat(ret)
})
output$tbl <- renderDataTable({
  dat()
})

根据表的模式,您可能只想考虑下载最新的数据。例如,如果有一个时间戳(例如,Created 表示数据插入表中的时间),那么您可以使用类似以下方式:

dat <- reactiveValue(NULL)
observeEvent(invalidateLater(2000), {
  olddat <- dat()
  latest <- max(c(as.POSIXct("1900-01-01"), olddat$Created), na.rm = TRUE)
  QUERY1 = "SELECT * FROM table t where t.Created > ?"
  newdat <- dbGetQuery(storiesDb, QUERY1, params = list(latest))
  if (NROW(newdat) > 0) {
    newdat <- rbind(olddat, newdat)
    dat(newdat)
  }
})
output$tbl <- renderDataTable({
  dat()
})

根据您的模式,表中可能还有一个“Modified”字段,其中“Created”表示首次添加的时间,“Modified”表示最后更改的时间。在这种情况下,您可能需要检查两个字段的最新性。(也许我现在有点超前了。)
然而,您可以使用reactivePoll来执行“快速”检查函数和“较慢”的值函数,例如:
df <- reactivePoll(2000, session,
  checkFunc = function() {
    dbGetQuery(storiesDb, "select count(*) as n from table")
  },
  valueFunc = function() {
    dbGetQuery(storiesDb, "select * from table")
  }  
})
output$tbl <- renderDataTable({
  df()
})

这个想法(使用valueFunc来计算行数)也可以适应CreatedModified,实际上,如果您拥有其中一个或两个字段,则大多数表格将被索引/优化,以便查询max(Created)非常快速。当然,获取表的行数通常被优化为近乎立即的查询,因此您可能会从一些内部基准测试和/或与您的DBA讨论中受益。

0

reactivePoll 返回一个响应式对象,因此您只能在响应式上下文中访问它。如果您想对其进行子集操作,可以使用以下编码样式:

df_subset <- reactive({
  df()[, input$selected_col]
})

这将再次返回一个响应式对象,因此您可以使用以下方式显示它:

output$table <- renderTable({
  df_subset()
})

如果你想在df_subset改变时对数据库进行更改,你可以使用observeEvent

observeEvent(subset_df(), {
  # code to change the DB
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接