多个selectInput值创建意外的dplyr(postgres)行为

时间:2016-06-02 11:51:30

标签: r postgresql shiny

我有一个可爱的Shiny应用程序,它接受selectInput值,查询postgres数据库,并输出图表。 (它是一个简单的界面,但由于dplyr数据库连接很难在这里重现!)

今天我将第一个selectInput值更改为multiple = TRUE;将传递给数据库的变量更新为修改后的控件返回的列表的%in%;所有的一切都松了。

  1. 在我选择一个值之前,控件为空,所以我会被Shiny red" ERROR:RS-DBI驱动程序..."告诉我我的询问" IN()"没有效率
  2. 当只选择一个值时,我会收到语法错误,"" locationID" IN' A1080330' "
  3. 我可以通过在返回的列表周围加上括号来解决这个问题... locationID%in%(输入$ rtnLocid)
  4. 然而,这会产生一个新的"运营商不存在"因为添加了括号而选择了多个列表项时出错:" IN((' A1080330',' B ...'))
  5. 我认为正在发生的事情是postgres驱动程序在使用IN时总是希望括号中的SQL列表值(某些DB在这里可能更宽松);添加括号可修复第一个选择;当启用多选时,添加的括号会再次破坏postgres驱动程序。

    其他任何使用Shiny / postgres的人都可以验证这种行为吗?

    此致 杰夫

    更新:@Steven在评论中指出了这个信息链接,我在发布时找不到:https://github.com/hadley/dplyr/issues/511

2 个答案:

答案 0 :(得分:4)

问题是当您仅选择一个元素并使用IN运算符时构建查询的方式。对dplyr的{​​{1}}转换不会添加正确的括号,因此会失败。我们详细讨论了这个问题here

解决此问题的一种方法是在SQL输入等于1时向filter()传递不同的指令(参见下面的示例)。

以下是发生的事情:

length

提供正确的tbl(mydb, "iris") %>% filter(Species %in% c("setosa", "versicolor")) %>% .$query 查询语法:

SQL

并且,如果执行,则给出预期的:

<Query> SELECT "Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width", "Species"
FROM "iris"
WHERE "Species" IN ('setosa', 'versicolor')
<PostgreSQLConnection>

让我们看看如果您尝试传递单个元素会发生什么:

#Source: postgres 9.3.13 [elm@127.0.0.1:5432/csvdump]
#From: iris [100 x 5]
#Filter: Species %in% c("setosa", "versicolor") 
#
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#          (dbl)       (dbl)        (dbl)       (dbl)   (chr)
#1           5.1         3.5          1.4         0.2  setosa
#2           4.9         3.0          1.4         0.2  setosa
#3           4.7         3.2          1.3         0.2  setosa
#4           4.6         3.1          1.5         0.2  setosa
#5           5.0         3.6          1.4         0.2  setosa
#6           5.4         3.9          1.7         0.4  setosa
#7           4.6         3.4          1.4         0.3  setosa
#8           5.0         3.4          1.5         0.2  setosa
#9           4.4         2.9          1.4         0.2  setosa
#10          4.9         3.1          1.5         0.1  setosa
#..          ...         ...          ...         ...     ...

查询将是:

tbl(mydb, "iris") %>%
  filter(Species %in% "setosa") %>%
  .$query

如果执行,将导致以下错误:

  

postgresqlExecStatement(conn,statement,...)出错:RS-DBI   驱动程序:(无法检索结果:错误:语法错误在或   靠近''setosa'“LINE 3:WHERE”Species“IN'setosa')AS”master“                              ^)

这是因为对于单个元素,对<Query> SELECT "Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width", "Species" FROM "iris" WHERE "Species" IN 'setosa' <PostgreSQLConnection> 查询的dplyr转换不会添加正确的括号。请注意它是SQL而不是'setosa'

为了规避这一点,我们可以做到:

('setosa')

这将构建一个语法上有效的if(length(input$Species) == 1) { tbl(mydb, "iris") %>% filter(Species == input$Species) %>% } 查询:

SQL

以下示例适用于此问题。在这里,我只是指示应用<Query> SELECT "Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width", "Species" FROM "iris" WHERE "Species" = 'setosa' <PostgreSQLConnection> 如果filter(Species == ...)input$Species 1,则为length

enter image description here

<强> ShinyApp

filter(Species %in% ...)

答案 1 :(得分:3)

尽管史蒂文的答案非常好,但我使用了一种略微不同的方法,允许您使用单个管道。当您想要执行其他操作时,这很方便。

这就是selectedData被动反应的样子:

selectedData <- reactive({
  tbl(mydb, "iris") %>% 
  {
    if(length(input$Species) == 1) 
      filter(., Species == input$Species)
    else
      filter(., Species %in% input$Species)
  } %>%
    data.frame %>%
    mutate() %>%
    etc... 
})