带有IN子句的R中的参数化SQL查询

时间:2016-08-24 12:32:25

标签: sql r rodbc parameterized-query

我正在尝试从Vertica DB通过RODBC包获取数据。我目前有一个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'clasue中传递的数据?

“IN”子句中传递的数据元素数量也非常多(超过100个不同的项目)。

是否可以从外部Vector或文件传递?

2 个答案:

答案 0 :(得分:2)

你是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

您可能需要摆弄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传递格式不正确的字符串的问题的影响,但它可能比在我显示的第一种方法中运行数百个查询更快。

答案 1 :(得分:0)

使用字符串操作执行此操作,如问题所示:

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'))` )")