Rmarkdown - оценить или отобразить параметризованный запрос блока SQL - PullRequest
0 голосов
/ 29 января 2020

Я использую параметризованные SQL запросы в моем файле Rmarkdown для обеспечения безопасности, а также для удобства чтения.

В конце концов, я получаю запросы, которые включают в себя имена таблиц динамических c, параметры динамических c и даже динамический c текст (с использованием логически оцененных переменных), например:
(динамические c переменные могут быть определены благодаря 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
'``

В случае длительного и сложный параметризованный запрос, он может быть очень полезен (для проверки и отладки) для доступа к полностью «оцененному» запросу. В нашем примере ожидаемый результат будет следующим:

"select * from flowers \ n, где color = 'red' \ n - и имя в ('rose', 'tulip') \ n предел 10 "

или

select * from flowers 
where color = 'red' 
-- and name in ('rose', 'tulip') 
limit 10

Но я понятия не имею, как это сделать ... Игра с параметрами чанка не помогла. Любой другой вариант?

1 Ответ

0 голосов
/ 03 февраля 2020

В конце концов я придумал свой ответ. Я опубликую его здесь, может быть когда-нибудь кому-нибудь пригодится.

Я просто повторно использовал функцию интерполяции из sql функции двигателя из knitr (можно найти здесь ) чтобы определить мою функцию:

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, чтобы получить правильное значение:

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")

, которое возвращает ожидаемый результат:

выберите * из цветов
, где цвет = 'красный'
- и имя в ('роза', 'тюльпан')
предел 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
...