在 R 中为 DBI 查询声明变量到 MS SQL
Declaring variable in R for DBI query to MS SQL
我正在编写一个 R 查询,其中 运行 有几个 SQL 查询使用 DBI 包来创建报告。为了完成这项工作,我需要能够在 R 中声明一个变量(例如 Period End Date),然后从 within SQL 查询中调用该变量。当我 运行 我的查询时,出现以下错误:
如果我只使用字段名称 (PeriodEndDate),我会收到以下错误:
Error in (function (classes, fdef, mtable) : unable to find an
inherited method for function ‘dbGetQuery’ for signature ‘"Microsoft
SQL Server", "character"’
如果我使用@访问字段名(@PeriodEndDate),我得到以下错误:
Error: nanodbc/nanodbc.cpp:1655: 42000: [Microsoft][ODBC SQL Server
Driver][SQL Server]Must declare the scalar variable "@PeriodEndDate".
[Microsoft][ODBC SQL Server Driver][SQL Server]Statement(s) could not
be prepared. '
示例查询可能如下所示:
library(DBI) # Used for connecting to SQL server and submitting SQL queries.
library(tidyverse) # Used for data manipulation and creating/saving CSV files.
library(lubridate) # Used to calculate end of month, start of month in queries
# Define time periods for queries.
PeriodEndDate <<- ceiling_date(as.Date('2021-10-31'),'month') # Enter Period End Date on this line.
PeriodStartDate <<- floor_date(PeriodEndDate, 'month')
# Connect to SQL Server.
con <- dbConnect(
odbc::odbc(),
driver = "SQL Server",
server = "SERVERNAME",
trusted_connection = TRUE,
timeout = 5,
encoding = "Latin1")
samplequery <- dbGetQuery(con, "
SELECT * FROM [TableName]
WHERE OrderDate <= @PeriodEndDate
")
我认为一种方法可能是使用粘贴功能,如下所示:
samplequery <- dbGetQuery(con, paste("
SELECT * FROM [TableName]
WHERE OrderDate <=", PeriodEndDate")
但是,如果涉及 多个 变量在查询外或在查询内的多个位置被引用,这可能会变得笨拙。
有没有相对简单的方法来做到这一点?
提前感谢您的任何想法!
大多数基于 DBI
的连接中的机制是在查询中使用 ?
-占位符[1],在中使用 params=
调用 DBI::dbGetQuery
或 DBI::dbExecute
.
也许是这样:
samplequery <- dbGetQuery(con, "
SELECT * FROM [TableName]
WHERE OrderDate <= ?
", params = list(PeriodEndDate))
一般来说,https://db.rstudio.com/best-practices/run-queries-safely/ 中很好地列举了将 R 对象作为 data-item 包含的机制。按照我推荐的顺序,
- 参数化查询(如上图);
glue::glue_sql
;
sqlInterpolate
(使用与#1 相同的 ?
占位符);
- link 也提到了使用
dbQuoteString
的“手动转义”。
由于疏忽,我认为其他任何事情都比较冒险 SQL corruption/injection.
我在这里看到很多关于尝试使用以下技术之一的问题:paste
and/or sprintf
使用 sQuote
或 hard-coded paste0("'", PeriodEndDate, "'")
。这些在我看来太脆弱了,应该避免。
我对参数化查询的偏好超出了这种可用性,它还会对重复使用同一查询产生 non-insignificant 影响,因为 DBMS 倾向于 analyze/optimize 查询并将其缓存以供下一次使用采用。考虑一下:
### parameterized queries
DBI::dbGetQuery("select ... where OrderDate >= ?", params=list("2020-02-02"))
DBI::dbGetQuery("select ... where OrderDate >= ?", params=list("2020-02-03"))
### glue_sql
PeriodEndDate <- as.Date("2020-02-02")
qry <- glue::glue_sql("select ... where OrderDate >= {PeriodEndDate}", .con=con)
# <SQL> select ... where OrderDate >= '2020-02-02'
DBI::dbGetQuery(con, qry)
PeriodEndDate <- as.Date("2021-12-22")
qry <- glue::glue_sql("select ... where OrderDate >= {PeriodEndDate}", .con=con)
# <SQL> select ... where OrderDate >= '2021-12-22'
DBI::dbGetQuery(con, qry)
在参数化查询的情况下,“查询”本身永远不会改变,因此可以重用其优化查询(服务器内部)。
在 glue_sql
查询的情况下,查询本身会发生变化(尽管只是少数字符),因此大多数(所有?)DBMS 将 re-analyze 和 re-optimize询问。虽然他们倾向于快速完成,并且大多数分析师的查询并不复杂,但它仍然是不必要的开销,并且在您的查询 and/or 索引需要更多工作来优化的情况下错过机会 嗯.
备注:
?
被大多数 DBMS 使用,但不是全部。其他人使用 $name
或 </code> 等。但是,对于 <code>odbc::odbc()
,它始终是 ?
(无名称,无编号),而不管实际的 DBMS。
不确定你是否在别处使用这个,但是使用<<-
(副<-
或=
)会助长坏习惯and/or unreliable/unexpected 个结果。
在一个查询中多次使用同一个变量的情况并不少见。不幸的是,您将需要多次包含该变量,并且顺序很重要。例如,
samplequery <- dbGetQuery(con, "
SELECT * FROM [TableName]
WHERE OrderDate <= ?
or (SomethingElse = ? and OrderDate > ?)0
", params = list(PeriodEndDate, 99, PeriodEndDate))
如果您有 list/vector 个值并想使用 SQL 的 IN
运算符,那么您有两个选择,我的偏好是第一个(出于上述原因):
创建一串问号并粘贴到查询中。 (是的,这是 paste
ing 到查询中,但我们没有处理不正确 single-quoting 或 double-quoting 的风险。因为 DBI
不支持任何其他机制,这就是我们所拥有的。)
MyDates <- c(..., ...)
qmarks <- paste(rep("?", length(MyDates)), collapse=",")
samplequery <- dbGetQuery(con, sprintf("
SELECT * FROM [TableName]
WHERE OrderDate IN (%s)
", qmarks), params = as.list(MyDates))
glue_sql
支持内扩:
MyDates <- c(..., ...)
qry <- glue::glue_sql("
SELECT * FROM [TableName]
WHERE OrderDate IN ({MyDates*})", .con=con)
DBI::dbGetQuery(con, qry)
我正在编写一个 R 查询,其中 运行 有几个 SQL 查询使用 DBI 包来创建报告。为了完成这项工作,我需要能够在 R 中声明一个变量(例如 Period End Date),然后从 within SQL 查询中调用该变量。当我 运行 我的查询时,出现以下错误:
如果我只使用字段名称 (PeriodEndDate),我会收到以下错误:
Error in (function (classes, fdef, mtable) : unable to find an inherited method for function ‘dbGetQuery’ for signature ‘"Microsoft SQL Server", "character"’
如果我使用@访问字段名(@PeriodEndDate),我得到以下错误:
Error: nanodbc/nanodbc.cpp:1655: 42000: [Microsoft][ODBC SQL Server Driver][SQL Server]Must declare the scalar variable "@PeriodEndDate". [Microsoft][ODBC SQL Server Driver][SQL Server]Statement(s) could not be prepared. '
示例查询可能如下所示:
library(DBI) # Used for connecting to SQL server and submitting SQL queries.
library(tidyverse) # Used for data manipulation and creating/saving CSV files.
library(lubridate) # Used to calculate end of month, start of month in queries
# Define time periods for queries.
PeriodEndDate <<- ceiling_date(as.Date('2021-10-31'),'month') # Enter Period End Date on this line.
PeriodStartDate <<- floor_date(PeriodEndDate, 'month')
# Connect to SQL Server.
con <- dbConnect(
odbc::odbc(),
driver = "SQL Server",
server = "SERVERNAME",
trusted_connection = TRUE,
timeout = 5,
encoding = "Latin1")
samplequery <- dbGetQuery(con, "
SELECT * FROM [TableName]
WHERE OrderDate <= @PeriodEndDate
")
我认为一种方法可能是使用粘贴功能,如下所示:
samplequery <- dbGetQuery(con, paste("
SELECT * FROM [TableName]
WHERE OrderDate <=", PeriodEndDate")
但是,如果涉及 多个 变量在查询外或在查询内的多个位置被引用,这可能会变得笨拙。
有没有相对简单的方法来做到这一点?
提前感谢您的任何想法!
大多数基于 DBI
的连接中的机制是在查询中使用 ?
-占位符[1],在中使用 params=
调用 DBI::dbGetQuery
或 DBI::dbExecute
.
也许是这样:
samplequery <- dbGetQuery(con, "
SELECT * FROM [TableName]
WHERE OrderDate <= ?
", params = list(PeriodEndDate))
一般来说,https://db.rstudio.com/best-practices/run-queries-safely/ 中很好地列举了将 R 对象作为 data-item 包含的机制。按照我推荐的顺序,
- 参数化查询(如上图);
glue::glue_sql
;sqlInterpolate
(使用与#1 相同的?
占位符);- link 也提到了使用
dbQuoteString
的“手动转义”。
由于疏忽,我认为其他任何事情都比较冒险 SQL corruption/injection.
我在这里看到很多关于尝试使用以下技术之一的问题:paste
and/or sprintf
使用 sQuote
或 hard-coded paste0("'", PeriodEndDate, "'")
。这些在我看来太脆弱了,应该避免。
我对参数化查询的偏好超出了这种可用性,它还会对重复使用同一查询产生 non-insignificant 影响,因为 DBMS 倾向于 analyze/optimize 查询并将其缓存以供下一次使用采用。考虑一下:
### parameterized queries
DBI::dbGetQuery("select ... where OrderDate >= ?", params=list("2020-02-02"))
DBI::dbGetQuery("select ... where OrderDate >= ?", params=list("2020-02-03"))
### glue_sql
PeriodEndDate <- as.Date("2020-02-02")
qry <- glue::glue_sql("select ... where OrderDate >= {PeriodEndDate}", .con=con)
# <SQL> select ... where OrderDate >= '2020-02-02'
DBI::dbGetQuery(con, qry)
PeriodEndDate <- as.Date("2021-12-22")
qry <- glue::glue_sql("select ... where OrderDate >= {PeriodEndDate}", .con=con)
# <SQL> select ... where OrderDate >= '2021-12-22'
DBI::dbGetQuery(con, qry)
在参数化查询的情况下,“查询”本身永远不会改变,因此可以重用其优化查询(服务器内部)。
在 glue_sql
查询的情况下,查询本身会发生变化(尽管只是少数字符),因此大多数(所有?)DBMS 将 re-analyze 和 re-optimize询问。虽然他们倾向于快速完成,并且大多数分析师的查询并不复杂,但它仍然是不必要的开销,并且在您的查询 and/or 索引需要更多工作来优化的情况下错过机会 嗯.
备注:
?
被大多数 DBMS 使用,但不是全部。其他人使用$name
或</code> 等。但是,对于 <code>odbc::odbc()
,它始终是?
(无名称,无编号),而不管实际的 DBMS。不确定你是否在别处使用这个,但是使用
<<-
(副<-
或=
)会助长坏习惯and/or unreliable/unexpected 个结果。在一个查询中多次使用同一个变量的情况并不少见。不幸的是,您将需要多次包含该变量,并且顺序很重要。例如,
samplequery <- dbGetQuery(con, " SELECT * FROM [TableName] WHERE OrderDate <= ? or (SomethingElse = ? and OrderDate > ?)0 ", params = list(PeriodEndDate, 99, PeriodEndDate))
如果您有 list/vector 个值并想使用 SQL 的
IN
运算符,那么您有两个选择,我的偏好是第一个(出于上述原因):创建一串问号并粘贴到查询中。 (是的,这是
paste
ing 到查询中,但我们没有处理不正确 single-quoting 或 double-quoting 的风险。因为DBI
不支持任何其他机制,这就是我们所拥有的。)MyDates <- c(..., ...) qmarks <- paste(rep("?", length(MyDates)), collapse=",") samplequery <- dbGetQuery(con, sprintf(" SELECT * FROM [TableName] WHERE OrderDate IN (%s) ", qmarks), params = as.list(MyDates))
glue_sql
支持内扩:MyDates <- c(..., ...) qry <- glue::glue_sql(" SELECT * FROM [TableName] WHERE OrderDate IN ({MyDates*})", .con=con) DBI::dbGetQuery(con, qry)