我正在通过ODBC连接到SQL服务器来查询数据和构建报告。现在我有一个脚本,它的工作方式完全应该呈现。rmd文件,并将其放在文件夹中,但我正在研究一个更精简的方式,通过一个函数来完成它。
函数的基础如下:
RenderFxn = function(RMDfile, OutputName){
source = "path/to/scripts/"
output = "path/to/output/location/"
rmarkdown::render(input = paste0(source,RMDfile,".Rmd"),
output_file = paste0(output,OutputName,"_",DATE))
}
RenderFxn("CreateReport","ReportOutput")
如果我逐行执行,它可以工作,但是一旦我通过函数调用它,我就会得到一个错误。错误发生在通过ODBC连接连接到SQL服务器的第一个代码块上。但是,如果我不通过函数调用它,它会工作得很好。
有谁知道这是为什么吗?
谢谢!
这里有几个问题,我推断其中大多数缺乏代码。这里有一个提示:
-
DATE
未定义,错误。 -
我要猜测Rmarkdown文档无法查询您需要的数据。请注意,渲染过程是在一个干净的环境中进行的,因此,如果在全局(调用)环境中有
值得注意的例外是conn
、mydata
、param1
,甚至DATE
等对象,则需要通过render(.., params=list(...))
参数传递几乎所有这些对象。参见https://bookdown.org/yihui/rmarkdown/parameterized-reports.html。conn
(无论是来自DBI::dbConnect
还是RODBC::
什么的)不能在这里传递,以及包含内存<pointer>
的类似对象。要解决这个问题,需要传递给DBI::dbConnect
的参数,并在Rmarkdown文档中重新创建连接对象。 -
(次要)在形成路径的目录组件时使用
file.path
而不是paste0
,这样你就不太可能忘记末尾的斜杠。 -
你的函数应该接受更多的参数,因为它打破了作用域,并试图找到它不能保证找到的东西。一般来说,应该显式地传递函数所需的内容。当然也有例外,但是如果没有明确的参数,你的函数就不是严格可复制的,排除故障是一件痛苦的事情,其他用户/读者也不知道需要什么,应该找到什么,应该是什么样子,等等(通过"用户",我在你忘记细节后的六个月内包括你)。
我建议这样一个函数:
RenderFunc <- function(rmd, outputname, ..., date = Sys.Date(), source = "path/to/scripts/", outputpath = "path/to/output/location/") { params <- c(list(...), date = date) if (!endsWith(rmd, ".Rmd")) rmd <- paste0(rmd, ".Rmd") outputname <- paste(OutputName, date, sep = "_") rmarkdown::render(input = file.path(source, rmd), output_file = file.path(output, outputname), params = params) } RenderFunc("CreateReport", "ReportOutput", data=mydata, dbuser="myuser", dbpass="mypass", dbhost="somehost")
在这种情况下,
data=
是在markdown文档中定义的参数,db*
变量将在params
的list(...)
部分中传递,以便Rmd可以实例化连接。
目标Rmarkdown文档的一个活生生的例子:
---
title: My Document
output: html_document
params:
date: !r Sys.Date()
data:
dbuser:
dbpass:
dbhost:
---
```{r setup, echo = FALSE, include = FALSE}
library(DBI)
conn <- dbConnect(uid = params$dbuser, pwd = params$dbpass, server = params$server)
```
# Some Header
```{r block1}
remotedata <- dbGetQuery(conn, "select ...")
# do something here
```
在这个例子中,我假设你正在传递data=
(某种形式的帧,也许)以及从数据库查询。你想要什么由你决定。
我通常不喜欢把DBI代码放在标记文档中;它可以工作,并且有很多用例,当它是绝对有意义的,但有时它可以更简单的Rmd只是处理数据,而不是连接和查询等。为此,您可以将该函数修改为:
RenderFunc2 <- function(rmd, outputname, ...,
dbuser, dbpass, dbhost,
date = Sys.Date(),
source = "path/to/scripts/",
outputpath = "path/to/output/location/") {
params <- c(list(...), date = date)
if (!endsWith(rmd, ".Rmd")) rmd <- paste0(rmd, ".Rmd")
outputname <- paste(OutputName, date, sep = "_")
conn <- DBI::dbConnect(uid=dbuser, pwd=dbpass, server=dbhost)
on.exit(dbDisconnect(conn))
mydata <- DBI::dbGetQuery(conn, "select ...")
params$data <- mydata
rmarkdown::render(input = file.path(source, rmd),
output_file = file.path(output, outputname),
params = params)
}
RenderFunc("CreateReport", "ReportOutput", data=mylocaldata,
dbuser="myuser", dbpass="mypass", dbhost="somehost")
在这种情况下,db*
参数仅由RenderFunc
使用,而不传递给Rmd文档(这将而不是试图创建db连接)。