2016-08-24 70 views
0

我正试图通过Vertica DB中的RODBC包提取数据。我目前有一个SQL查询,如下所示。带有IN子句的R中的参数化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个不同的项目)。

如果它可以从外部矢量或文件传递,有没有办法?

+2

到OP和未来的读者 - 请注意OP的尝试或接受的答案都不是真正的参数化SQL查询。 @Benjamin是一个更好的尝试,而其他人只是简单地连接一个动态SQL字符串。 – Parfait

回答

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

我们可以交替使用fn$从gsubfn包串插:

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