我正在使用 SQL 和用户输入。所以我使用这个glue
库来处理参数化查询。
但是,为了保持清洁,我将所有内容包装在一个函数中:
safeQuery <- function(con, sql, ...) {
sql = glue_sql(sql, ..., .con=con)
query <- dbSendQuery(con, sql)
out <- dbFetch(query)
dbClearResult(query)
return(out)
}
glue_sql
因此,我只需使用连接、SQL 代码和适当绑定的 SQL 代码的参数列表来调用该函数。
这工作得很好。
现在,我有一个特定的 SQL 调用,我经常以一种或另一种方式使用它,但参数不同。
所以我决定为此创建一个函数:
get_data <- function(con, params) {
safeQuery(con,
"SELECT *
FROM foo
WHERE bar IN ({vars*})",
vars=params)
}
p = c(1, 2)
get_data(con, p)
因此,用户数据(在本例中c(1, 2)
)将被传递给get_data
,这将与 SQL 调用一起传递给safeQuery
,其中glue_sql
将处理绑定。
但是,如果我真的尝试运行get_data
,我会得到一个错误
object 'params' not found
谷歌搜索和 SO'ing 已经清楚地表明这与 R 的惰性评估有关。
事实上,get_data
改为
get_data <- function(con, params) {
do.call("safeQuery",
list(con,
"SELECT *
FROM foo
WHERE bar IN ({vars*})",
vars=params)
}
(正如这个答案所推荐的)工作得很好,因为do.call
在将它们发送到之前评估列表中的参数safeQuery
。
我不明白为什么这首先是必要的。毕竟, 的值params
不会在任何一步被修改到glue_sql
,所以它应该仍然可用。
链接的答案讨论了使用substitute
(我还阅读了有关该主题的这篇 R-bloggers 帖子)将参数的名称替换为调用者的名称(或者如果直接给出参数值,则使用其实际值),但这并没有在我的情况下不起作用。修改get_data
使用substitute
:
get_data <- function(con, params) {
do.call("safeQuery",
list(con,
"SELECT *
FROM foo
WHERE bar IN ({vars*})",
vars=substitute(params))
}
导致以下 SQL 来自glue_sql
:
SELECT *
FROM foo
WHERE bar IN (params)
而不是 的实际值params
。我无法在其中尝试相同的方法,safeQuery
因为参数隐藏在...
其中并且substitute(...)
不起作用。我试过了。
我也尝试过force(params)
在开头调用get_data
,但这给出了同样的object not found
错误。
get_data <- function(con, params) {
force(params)
do.call("safeQuery",
list(con,
"SELECT *
FROM foo
WHERE bar IN ({vars*})",
vars=params)
}
那么,为什么会params
在标准调用中“迷失”呢?为什么do.call
有效,但无效force(params)
?是否可以使用标准评估来完成这项工作?
而且我不会撒谎:这种经历让我对如何编写函数和处理它们的参数感到困惑(我正在考虑do.call
从现在开始只使用)。如果可以在不过度扩展这个问题的范围的情况下给出提示,我将非常感激。