R如何编写像SAS这样的宏

Posted

技术标签:

【中文标题】R如何编写像SAS这样的宏【英文标题】:How R write a Macro like 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, " 【参考方案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 的第一个参数似乎是连接对象或类似对象,而不是查询字符串。

以上是关于R如何编写像SAS这样的宏的主要内容,如果未能解决你的问题,请参考以下文章

sas 宏的问题

SAS 程序冷知识——如何在启动sas程序时自动执行一些宏?

如何编写要在 SAS 中导入的原始数据

SAS 的导入导出 excel 表格的实现

如何使用 SAS 读取 Azure databricks 中的 blob

如何转换要在 R 中打开的 SAS (.sas) 文件? [关闭]