R 中带有 IN 子句的参数化 SQL 查询
Parameterized SQL query in R with IN clause
我正在尝试通过 RODBC 包从 Vertica DB 获取数据。我目前有一个 SQL 查询,如下所示。
library(rodbc) channel = odbcconnect("VerticaDB") query = paste
(
SELECT *
FROM item_history
WHERE item_exp_date BETWEEN ",x," AND ",y,"
AND item_code IN ('A1',
'A2',
'B1',
'B2')",sep="")result = (sqlQuery(channel,query)
)
我已经能够参数化 'BETWEEN' 子句中传递的数据。有没有一种方法可以参数化 'IN' 子句中传递的数据?
'IN' 子句中传递的数据元素数量也非常多(超过 100 个不同的项目)。
有没有办法从外部Vector或者文件传过来?
你得到的是 SQL 注入而不是参数化查询。您可能需要查看 RODBCext
包及其 vignette.
要正确参数化查询,您可以
library(RODBC)
library(RODBCext)
channel = odbcConnect("VerticaDB")
query = paste0("select * from Item_History ",
"where Item_Exp_Date between ? and ? ",
"and Item_Code = ?")
item <- c("A1", "A2", "B1", "B2")
x <- 3
y <- 10 # I don't actually know what your x and y are, but hopefully you get the idea
sqlExecute(
channel = channel,
query = query,
data = list(x = rep(x, length(item)),
y = rep(y, length(item)),
item = item),
fetch = TRUE,
stringsAsFactors = FALSE
)
然而,这有很大的缺点,因为 sqlExecute
将 运行 对 data
参数中的每一行进行查询(列表将被强制转换为数据框)。如果您的 item
向量中有数百个元素,您将 运行 对您的 SQL 实例进行数百次查询,这可能不是特别有效。
一种不太明显的方法是编写一个存储过程来构造查询。
您在 SQL 中的存储过程可能类似于
CREATE PROCEDURE schema.specialQuery
@x int;
@y int;
@in varchar(2000);
AS
BEGIN
DECLARE @query = varchar(8000);
SET @query = 'select * from Item_History ' +
'where Item_Exp_Date between ' + convert(@x, varchar(10)) +
' and ' + convert(@y, varchar(10)) +
' and Item_Code IN (' + @in ')'
EXEC @query
END
GO
您可能需要 fiddle 使用 convert
函数和一些引号,但它适用于
sqlExecute(
channel = channel,
query = "EXECUTE schema.specialQuery @x = ?, @y = ?, @in = ?",
data = list(x = x,
y = y,
in = sprintf("'%s'", paste0(item, collapse = "', '"))),
fetch = TRUE,
stringsAsFactors = FALSE
)
不幸的是,这种方法仍然容易受到通过 item
传递格式不正确的字符串的问题,但它可能比 运行 我展示的第一种方法中的数百个查询更快。
要像问题中那样使用字符串操作来做到这一点:
x <- "2000-01-01"
y <- "2001-01-01"
Item_Code <- c('A1','A2','B1','B2')
query <- sprintf("select * from Item_History
where Item_Exp_Date between '%s' and '%s'
and Item_Code in (%s)", x, y, toString(shQuote(Item_Code, 'sh')))
我们可以交替使用 gsubfn 包中的 fn$
进行字符串插值:
library(gsubfn)
query2 <- fn$identity("select * from Item_History
where Item_Exp_Date between '$x' and '$y'
and Item_Code in ( `toString(shQuote(Item_Code, 'sh'))` )")
我正在尝试通过 RODBC 包从 Vertica DB 获取数据。我目前有一个 SQL 查询,如下所示。
library(rodbc) channel = odbcconnect("VerticaDB") query = paste
(
SELECT *
FROM item_history
WHERE item_exp_date BETWEEN ",x," AND ",y,"
AND item_code IN ('A1',
'A2',
'B1',
'B2')",sep="")result = (sqlQuery(channel,query)
)
我已经能够参数化 'BETWEEN' 子句中传递的数据。有没有一种方法可以参数化 'IN' 子句中传递的数据?
'IN' 子句中传递的数据元素数量也非常多(超过 100 个不同的项目)。
有没有办法从外部Vector或者文件传过来?
你得到的是 SQL 注入而不是参数化查询。您可能需要查看 RODBCext
包及其 vignette.
要正确参数化查询,您可以
library(RODBC)
library(RODBCext)
channel = odbcConnect("VerticaDB")
query = paste0("select * from Item_History ",
"where Item_Exp_Date between ? and ? ",
"and Item_Code = ?")
item <- c("A1", "A2", "B1", "B2")
x <- 3
y <- 10 # I don't actually know what your x and y are, but hopefully you get the idea
sqlExecute(
channel = channel,
query = query,
data = list(x = rep(x, length(item)),
y = rep(y, length(item)),
item = item),
fetch = TRUE,
stringsAsFactors = FALSE
)
然而,这有很大的缺点,因为 sqlExecute
将 运行 对 data
参数中的每一行进行查询(列表将被强制转换为数据框)。如果您的 item
向量中有数百个元素,您将 运行 对您的 SQL 实例进行数百次查询,这可能不是特别有效。
一种不太明显的方法是编写一个存储过程来构造查询。
您在 SQL 中的存储过程可能类似于
CREATE PROCEDURE schema.specialQuery
@x int;
@y int;
@in varchar(2000);
AS
BEGIN
DECLARE @query = varchar(8000);
SET @query = 'select * from Item_History ' +
'where Item_Exp_Date between ' + convert(@x, varchar(10)) +
' and ' + convert(@y, varchar(10)) +
' and Item_Code IN (' + @in ')'
EXEC @query
END
GO
您可能需要 fiddle 使用 convert
函数和一些引号,但它适用于
sqlExecute(
channel = channel,
query = "EXECUTE schema.specialQuery @x = ?, @y = ?, @in = ?",
data = list(x = x,
y = y,
in = sprintf("'%s'", paste0(item, collapse = "', '"))),
fetch = TRUE,
stringsAsFactors = FALSE
)
不幸的是,这种方法仍然容易受到通过 item
传递格式不正确的字符串的问题,但它可能比 运行 我展示的第一种方法中的数百个查询更快。
要像问题中那样使用字符串操作来做到这一点:
x <- "2000-01-01"
y <- "2001-01-01"
Item_Code <- c('A1','A2','B1','B2')
query <- sprintf("select * from Item_History
where Item_Exp_Date between '%s' and '%s'
and Item_Code in (%s)", x, y, toString(shQuote(Item_Code, 'sh')))
我们可以交替使用 gsubfn 包中的 fn$
进行字符串插值:
library(gsubfn)
query2 <- fn$identity("select * from Item_History
where Item_Exp_Date between '$x' and '$y'
and Item_Code in ( `toString(shQuote(Item_Code, 'sh'))` )")