我有一个可爱的Shiny应用程序,它接受selectInput值,查询postgres数据库,并输出图表。 (它是一个简单的界面,但由于dplyr数据库连接很难在这里重现!)
今天我将第一个selectInput值更改为multiple = TRUE;将传递给数据库的变量更新为修改后的控件返回的列表的%in%;所有的一切都松了。
我认为正在发生的事情是postgres驱动程序在使用IN时总是希望括号中的SQL列表值(某些DB在这里可能更宽松);添加括号可修复第一个选择;当启用多选时,添加的括号会再次破坏postgres驱动程序。
其他任何使用Shiny / postgres的人都可以验证这种行为吗?
此致 杰夫
更新:@Steven在评论中指出了这个信息链接,我在发布时找不到:https://github.com/hadley/dplyr/issues/511
答案 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
。
<强> 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...
})