Как R написать макрос, как SAS - PullRequest
0 голосов
/ 14 мая 2018

У меня действительно такой длинный sqlquery, и мне нужно запустить его несколько раз, только изменив дату.Я хорош в SAS, но довольно плохо знаком с R, поэтому я изо всех сил стараюсь написать что-то похожее на SAS.

df <- sqlQuery(datamart, paste0("Select xxxxxxxxxxxxxxxxxx from xxxxx
where date = '28Feb2018'"), as.is=TRUE, stringsAsFactors = FALSE)

Не могли бы вы поделиться опытом, пожалуйста?

Спасибо!


отредактировано 5/6/2018:

Я отредактировал код в соответствии с ответами ниже, и у меня все еще есть некоторые проблемы в правильном выполнении кода.в настоящее время мой код становится:

safeqry <- function(date_string)
{require(RODBCext)
qry_string <- paste0("DELETE FROM [T_SPP] WHERE [BkDt]<=#?","#")
  parms <- data.frame(date_string, stringsAsFactors=FALSE)
  sqlExecute(access, qry_string, parms, fetch=TRUE)}
safeqry('2016-04-30')

ошибка:

42000 -3100 [Microsoft] [ODBC Microsoft Access Driver] Синтаксическая ошибка в дате в выражении запроса '[BkDt] <= # Pa_RaM000.[RODBCext] Ошибка: ошибка SQLExecute. Дополнительно: Предупреждающее сообщение: в sqlExecute (access, qry_string, parms, fetch = TRUE): </p>

другой код:

query_delete = function (table, date_col, date) {
paste0('DELETE FROM [',table,'] WHERE [',date_col,']<=#',date, '#')}
sqlQuery(access, query_delete("T_SPP", "BkDt", "2018-04-30"),as.is = TRUE, stringsAsFactors = FALSE)

ошибкаis

[1] "[RODBC] ОШИБКА: не удалось SQLExecDirect 'УДАЛИТЬ ИЗ [T_SPP] ГДЕ [BkDt] <= # 2018-04-30 #'" </p>

Ответы [ 3 ]

0 голосов
/ 14 мая 2018

R-эквивалентом макроса SAS является функция. Таким образом, вы пишете функцию, которая принимает дату в качестве параметра, а затем передает дату в запрос.

Самый простой способ сделать это - манипулирование строками:

qry <- function(date_string)
{
    qry_string <- paste0("select xxxxx from yyy where date = '", date_string, "'")
    sqlQuery(datamart, qry_string, as.is=TRUE, stringsAsFactors=FALSE)
}

ОДНАКО, как правило, это небезопасно, поскольку люди могут передавать вредоносные строки в вашу функцию, заставляя ее совершать плохие вещи. Вместо этого рассмотрите возможность использования пакета RODBCext для выполнения параметризованных запросов, а не чем возиться со строками:

safeqry <- function(date_string)
{
    require(RODBCext)
    qry_string <- paste0("select xxxxx from yyy where date = ?")
    parms <- data.frame(date_string, stringsAsFactors=FALSE)
    sqlExecute(datamart, qry_string, parms, fetch=TRUE)
}
0 голосов
/ 14 мая 2018

Самый простой метод выглядит примерно так:

macro1 <- function(dt) {
  qry <- paste0("select xxxxxxxxxx from xxxx where date='", dt, "'")
  sqlQuery(datamart, qry, as.is=TRUE, stringsAsFactors=FALSE)
}

Но в этом есть пара вещей:

  • предполагается, что объект подключения datamart доступен в родительской (и / или глобальной) среде и является допустимым; если вы проводите тестирование с другими соединениями, я гарантирую, что это укусит вас так, как вы этого не ожидаете; и
  • очень склонен к SQL-инъекции (комикс: xkcd 327 )
  • если ваш аргумент пуст или имеет длину 2 или более, он может не выполнять то, что вы хотите

Немного более надежная функция выглядит примерно так:

macro2 <- function(dt, con) {
  if (length(dt) == 0L) {
    stop("'dt' is not length 1")
  } else if (length(dt) > 1L) {
    warning("'dt' has length > 1 and only the first element will be used")
    dt <- dt[[1L]]
  }
  qry <- sprintf("select xxxxxxxxxx from xxxx where date='%s'", sQuote(dt))
  sqlQuery(con, qry, as.is=TRUE, stringsAsFactors=FALSE)
}

хотя лучшее решение будет использовать привязку переменных («параметризованные запросы»), которая уникальна для каждого типа базы данных. Как предложил Hong Ooi, RODBCext предоставляет это для RODBC соединений, но в противном случае вам понадобится нечто более специфичное для базы данных.

Если вы хотите быть немного ленивым и чувствовать себя в безопасности, что соединение всегда будет глобальным, у вас может возникнуть соблазн сделать что-то вроде:

macro2 <- function(dt, con=datamart) ...

, который будет работать, но я все еще остерегаюсь этого. Опыт показывает, что явные часто намного безопаснее и их легче устранять.

Отсюда можно использовать цикл, будь то цикл for, как предложил JasonAizkalns, или, возможно, что-то вроде:

answers <- lapply(vector_of_dates, macro2)
0 голосов
/ 14 мая 2018

Есть несколько способов сделать это, но так как вы сказали, что вы новичок в R, это может быть естественным подходом.Во-первых, создайте функцию, которая позволяет вам изменять select_cols, tbl и date и возвращает строку:

make_query <- function(select_cols, tbl, date) {
  paste0("SELECT ", select_cols, " FROM ", tbl, " WHERE date = '", date, "';")
}

make_query("*", "my_table", "28Feb2018")
[1] "SELECT * FROM my_table WHERE date = '28Feb2018';"
make_query("*", "different_table", "28Feb2018")
[1] "SELECT * FROM different_table WHERE date = '28Feb2018';"

Затем вы можете создать вектор дат для циклического прохождения:

various_dates <- c("28Feb2018", "01Mar2018", "02Mar2018")

for (date in seq_along(various_dates)) {
  make_query("*", "my_table", various_dates[date])
}

Конечно, вы можете изменить тело цикла, чтобы использовать вашу sqlQuery функцию:

for (date in seq_along(various_dates)) {
  sqlQuery(datamart, make_query("*", "my_table", various_dates[date]), 
           as.is = TRUE, stringsAsFactors = FALSE)
}

И так как вы хотите сохранить результаты, вы можете предварительно-выделите пустой список такой же длины, как число дат, и сохраните результаты:

df <- vector("list", length(various_dates))
for (date in seq_along(various_dates)) {
  df[[date]] <- sqlQuery(datamart, make_query("*", "my_table", various_dates[date]), 
           as.is = TRUE, stringsAsFactors = FALSE)
}
...