【问题标题】:How R write a Macro like SASR如何编写像SAS这样的宏
【发布时间】:2018-05-14 15:27:14
【问题描述】:

我有一个很长的这样的sqlquery,只需要更改日期就需要运行几次。我擅长 SAS,但对 R 很陌生,所以我正在努力编写类似于 SAS 的东西。

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

你能分享一些经验吗?

谢谢!


于 2018 年 6 月 5 日编辑:

我根据下面的答案编辑了代码,但我仍然无法正确运行代码。 目前我的代码变成:

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]

另一个代码是

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)

错误是

[1]“[RODBC] 错误:无法 SQLExecDirect 'DELETE FROM [T_SPP] WHERE [BkDt]

【问题讨论】:

  • 您可以使用 for 循环。 for (date in dates) {...} 然后使用paste0date 粘贴到您的SQL 中。
  • C_Mu,其中一个答案能解决您的问题吗?
  • C_Mu,您是否有理由忽略这些答案?如果他们不能解决您的问题,我建议您说明他们为什么不起作用(足够)。如果至少有一个就够了,请accept the preferred answer
  • 我终于弄清楚了如何去做,这些答案给了我很多提示,这是最终的代码作品。谢谢大家在这里帮助我。
  • query_string = function(table, date_col_name, date) { x=paste0('Delete From ', table, ' Where ', date_col_name, "

标签: r macros rodbc


【解决方案1】:

相当于 SAS 宏的 R 是一个函数。因此,您编写了一个以日期为参数的函数,然后将日期传递给查询。

最简单的方法是通过字符串操作:

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

但是,这通常是不安全的,因为人们可以将恶意字符串传递给您的函数,导致它执行 Bad Things. 相反,请考虑使用 RODBCext package 来运行参数化查询而不是弄乱字符串:

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

【讨论】:

  • 这将是我的首选方法。除了规避 SQL 注入攻击之外,sqlExecute 还会为您管理重复。
【解决方案2】:

最简单的方法如下所示:

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

但它有几个问题:

  • 它假定连接对象datamart 在父(和/或全局)环境中可用并且有效;如果您正在使用不同的连接进行测试,我保证这会以您意想不到的方式咬住您;和
  • 很容易出现SQL injection(漫画: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 建议的那样,RODBCextRODBC 连接提供了此功能,但您需要更多特定于数据库的东西。

如果您想稍微懒一点,并且对始终在全局中的连接感到安全,您可能会尝试执行以下操作:

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

这会起作用,但我仍然提防它。经验表明,显式通常更安全,更容易排除故障。

从这里开始,可以使用循环,无论是 JasonAizkalns 建议的 for 循环,还是类似:

answers <- lapply(vector_of_dates, macro2)

【讨论】:

    【解决方案3】:

    有几种方法可以做到这一点,但既然你说你是R 的新手,这可能是一种自然的方法。首先,创建允许您更改select_colstbldate 的函数并返回一个字符串:

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

    【讨论】:

    • sqlQuery 的第一个参数似乎是连接对象或类似对象,而不是查询字符串。
    猜你喜欢
    • 1970-01-01
    • 2013-10-29
    • 1970-01-01
    • 1970-01-01
    • 2012-09-26
    • 1970-01-01
    • 1970-01-01
    • 2014-02-21
    • 1970-01-01
    相关资源
    最近更新 更多