Rmarkdown - 评估或显示 SQL 块的参数化查询
Rmarkdown - evaluate or display the parametized query of an SQL chunk
出于安全和可读性原因,我在我的 Rmarkdown 文件中使用参数化 SQL 查询。
最终,我提出了包含动态表名、动态参数甚至动态文本(使用逻辑评估变量)的查询,例如:
(感谢 glue_sql()
可能已经定义了动态变量)
```{sql, output.var = "data"}
select * from ?table_name
where color = ?color_value
?if_I_want_names and name in (?names_choices)
limit ?limit_value
'``
在长而复杂的参数化查询的情况下,访问完全 "evaluated" 查询可能非常有用(对于检查和调试)。在我们的示例中,预期结果将是:
"select * from flowers\n
where color = 'red' \n
-- and name in ('rose', 'tulip')\n
limit 10"
或
select * from flowers
where color = 'red'
-- and name in ('rose', 'tulip')
limit 10
但我不知道该怎么做...使用块参数没有帮助。还有其他选择吗?
最终,我想出了自己的答案。我将 post 放在这里,也许有一天对某人有用。
我简单地重新使用了 knitr
中 sql 引擎函数的插值函数(可以找到 here)来定义我的函数:
require('DBI')
require('knitr')
require('glue')
# Return char vector of sql interpolation param names
varnames_from_sql = function(conn = ANSI(), sql) {
varPos = DBI::sqlParseVariables(conn, sql)
if (length(varPos$start) > 0) {
varNames = substring(sql, varPos$start, varPos$end)
sub('^\?', '', varNames)
}
}
# Vectorized version of exists
mexists = function(x, env = knit_global(), inherits = TRUE) {
vapply(x, exists, logical(1), where = env, inherits = inherits)
}
# Interpolate a sql query based on the variables in an environment
interpolate_from_env = function(conn = ANSI(), sql, env = knit_global(), inherits = TRUE) {
names = unique(varnames_from_sql(conn, sql))
names_missing = names[!mexists(names, env, inherits)]
if (length(names_missing) > 0) {
stop("Object(s) not found: ", paste('"', names_missing, '"', collapse = ", "))
}
args = if (length(names) > 0) setNames(
mget(names, envir = env, inherits = inherits), names
)
do.call(DBI::sqlInterpolate, c(list(conn, sql), args))
}
然后,我可以将其称为我的 SQL 表达式以获得正确的 eval:
interpolate_from_env(sql = "select * from ?table_name
where color = ?color_value
?if_I_want_names and name in (?names_choices_sql)
limit ?limit_value")
哪个returns预期结果:
select * from flowers
where color = 'red'
-- and name in ('rose', 'tulip')
limit 10
注 :
此方法需要使用 glue_sql()
对变量进行适当的预评估。
在这种情况下:
table_name <- glue_sql("flowers")
color_value <- "red"
names_choices <-
names_choices_sql <- glue_sql("{c("rose", "tulip")*}", # the {}* allows to collapse a vector in SQL format
.con = ANSI())
if_I_want_names <- glue_sql("--")
limit_value <- 10
出于安全和可读性原因,我在我的 Rmarkdown 文件中使用参数化 SQL 查询。
最终,我提出了包含动态表名、动态参数甚至动态文本(使用逻辑评估变量)的查询,例如:
(感谢 glue_sql()
可能已经定义了动态变量)
```{sql, output.var = "data"}
select * from ?table_name
where color = ?color_value
?if_I_want_names and name in (?names_choices)
limit ?limit_value
'``
在长而复杂的参数化查询的情况下,访问完全 "evaluated" 查询可能非常有用(对于检查和调试)。在我们的示例中,预期结果将是:
"select * from flowers\n where color = 'red' \n -- and name in ('rose', 'tulip')\n limit 10"
或
select * from flowers
where color = 'red'
-- and name in ('rose', 'tulip')
limit 10
但我不知道该怎么做...使用块参数没有帮助。还有其他选择吗?
最终,我想出了自己的答案。我将 post 放在这里,也许有一天对某人有用。
我简单地重新使用了 knitr
中 sql 引擎函数的插值函数(可以找到 here)来定义我的函数:
require('DBI')
require('knitr')
require('glue')
# Return char vector of sql interpolation param names
varnames_from_sql = function(conn = ANSI(), sql) {
varPos = DBI::sqlParseVariables(conn, sql)
if (length(varPos$start) > 0) {
varNames = substring(sql, varPos$start, varPos$end)
sub('^\?', '', varNames)
}
}
# Vectorized version of exists
mexists = function(x, env = knit_global(), inherits = TRUE) {
vapply(x, exists, logical(1), where = env, inherits = inherits)
}
# Interpolate a sql query based on the variables in an environment
interpolate_from_env = function(conn = ANSI(), sql, env = knit_global(), inherits = TRUE) {
names = unique(varnames_from_sql(conn, sql))
names_missing = names[!mexists(names, env, inherits)]
if (length(names_missing) > 0) {
stop("Object(s) not found: ", paste('"', names_missing, '"', collapse = ", "))
}
args = if (length(names) > 0) setNames(
mget(names, envir = env, inherits = inherits), names
)
do.call(DBI::sqlInterpolate, c(list(conn, sql), args))
}
然后,我可以将其称为我的 SQL 表达式以获得正确的 eval:
interpolate_from_env(sql = "select * from ?table_name
where color = ?color_value
?if_I_want_names and name in (?names_choices_sql)
limit ?limit_value")
哪个returns预期结果:
select * from flowers
where color = 'red'
-- and name in ('rose', 'tulip')
limit 10
注 :
此方法需要使用 glue_sql()
对变量进行适当的预评估。
在这种情况下:
table_name <- glue_sql("flowers")
color_value <- "red"
names_choices <-
names_choices_sql <- glue_sql("{c("rose", "tulip")*}", # the {}* allows to collapse a vector in SQL format
.con = ANSI())
if_I_want_names <- glue_sql("--")
limit_value <- 10