R RODBCext和参数化IN语句?

时间:2016-07-29 18:45:31

标签: sql-server r rodbc

我一直在努力参数化一个在WHERE子句中使用IN语句的SQL语句。我使用rodbcext库进行参数化,但似乎没有扩展列表。

我希望编写诸如

之类的代码
sqlExecute("SELECT * FROM table WHERE name IN (?)", c("paul","ringo","john", "george")

我使用以下代码,但想知道是否有更简单的方法。

library(RODBC)
library(RODBCext)

# Search inputs
names <- c("paul", "ringo", "john", "george")

# Build SQL statement
qmarks <- replicate(length(names), "?")
stringmarks <- paste(qmarks, collapse = ",")
sql <- paste("SELECT * FROM tableA WHERE name IN (", stringmarks, ")")
# expand to Columns - seems to be the magic step required
bindnames <- rbind(names)

# Execute SQL statement
dbhandle <- RODBC::odbcDriverConnect(connectionString)
result <- RODBCext::sqlExecute(dbhandle, sql, bindnames, fetch = TRUE)
RODBC::odbcClose(dbhandle)

它有效,但我觉得我用R来以错误的方式扩展字符串(对R来说是新的 - 有很多方法可以做同样的错误)。有人可能会说&#34;这会产生因素 - 从来没有这样做&#34; : - )

我发现这篇文章表明我在正确的轨道上,但它没有讨论必须扩展&#34;?&#34;并将列表转换为data.frame

的列

R RODBC putting list of numbers into an IN() statement

谢谢。

更新:正如Benjamin在下面所示 - sqlExecute函数可以处理输入的list()。但是在检查生成的SQL时,我发现它使用游标来汇总结果。这显着增加了我在上面显示的示例代码上的CPU和I / O.

虽然图书馆确实可以为你解决这个问题,但是对于大的结果来说它可能太贵了。有两个答案,这取决于您的需求。

2 个答案:

答案 0 :(得分:1)

由于您查询中的唯一参数位于IN的集合中,因此您可以使用

sqlExecute(dbhandle,
    "SELECT * FROM table WHERE name IN (?)", 
    list(c("paul","ringo","john", "george")),
    fetch = TRUE)

sqlExecute会将列表中的值绑定到问号。在这里,它实际上将重复查询四次,对于向量中的每个值一次。这样做可能看起来有些愚蠢,但是当尝试传递字符串时,在许多方面让绑定负责设置适当的引用结构而不是尝试将其粘贴到自己中更安全。您将以这种方式生成更少的错误,并避免许多数据库安全问题。

答案 1 :(得分:0)

如果在字符对象中声明变量表然后与查询连接,该怎么办。

library(RODBC)
library(RODBCext)

# Search inputs
names <- c("paul", "ringo", "john", "george")

# Build SQL statement
sql_top <- paste0( "SET NOCOUNT ON \r\n DECLARE @LST_NAMES TABLE (ID NVARCHAR(20)) \r\n INSERT INTO @LST_NAMES VALUES ('", paste(names, collapse = "'), ('" ) , "')")

sql_body <- paste("SELECT * FROM tableA WHERE name IN (SELECT id FROM @LST_NAMES)")
sql <- paste0(sql_top, "\r\n", sql_body)

# Execute SQL statement
dbhandle <- RODBC::odbcDriverConnect(connectionString)
result <- RODBCext::sqlExecute(dbhandle, sql, bindnames, fetch = TRUE)
RODBC::odbcClose(dbhandle)

查询将是(不设置计数对检索结果很重要)

SET NOCOUNT ON 
DECLARE @LST_NAMES TABLE (ID NVARCHAR(20)) 
INSERT INTO @LST_NAMES VALUES ('paul'), ('ringo'), ('john'), ('george')
SELECT * FROM tableA WHERE name IN (SELECT id FROM @LST_NAMES)