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) ...
然后使用paste0
将date
粘贴到您的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 建议的那样,RODBCext
为RODBC
连接提供了此功能,但您需要更多特定于数据库的东西。
如果您想稍微懒一点,并且对始终在全局中的连接感到安全,您可能会尝试执行以下操作:
macro2 <- function(dt, con=datamart) ...
这会起作用,但我仍然提防它。经验表明,显式通常更安全,更容易排除故障。
从这里开始,可以使用循环,无论是 JasonAizkalns 建议的 for
循环,还是类似:
answers <- lapply(vector_of_dates, macro2)
【讨论】:
【参考方案3】:有几种方法可以做到这一点,但既然你说你是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)
【讨论】:
sqlQuery
的第一个参数似乎是连接对象或类似对象,而不是查询字符串。以上是关于R如何编写像SAS这样的宏的主要内容,如果未能解决你的问题,请参考以下文章
SAS 程序冷知识——如何在启动sas程序时自动执行一些宏?