R RODBCext 和参数化 IN 语句?
Posted
技术标签:
【中文标题】R RODBCext 和参数化 IN 语句?【英文标题】:R RODBCext and Parameterizing IN statement? 【发布时间】:2016-07-29 18:45:30 【问题描述】:我一直致力于参数化在 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 来说有点新 - 很多方法都做错了同样的事情)。有人可能会说“这会产生因素 - 永远不要那样做”:-)
我发现这篇文章表明我走在正确的轨道上,但它没有讨论必须扩展“?”并将列表转换为 data.frame 的列
R RODBC putting list of numbers into an IN() statement
谢谢。
更新:正如 Benjamin 下面所示 - sqlExecute 函数可以处理输入的 list()。然而,在检查生成的 SQL 后,我发现它使用游标来汇总结果。与我上面显示的示例代码相比,这显着增加了 CPU 和 I/O。
虽然该库确实可以为您解决这个问题,但对于大型结果,它可能过于昂贵。有两个答案,这取决于您的需求。
【问题讨论】:
在 php 中,我看到很多将变量直接放入 sql 语句的代码。如果它有效,你为什么要关心?并非所有事情在 R 中都很容易做到。这是我第一次看到 R 连接到数据库。 连接到数据库是 R 的主要功能之一 【参考方案1】:由于您在查询中的唯一参数是 IN
的集合,因此您可以侥幸逃脱
sqlExecute(dbhandle,
"SELECT * FROM table WHERE name IN (?)",
list(c("paul","ringo","john", "george")),
fetch = TRUE)
sqlExecute
会将列表中的值绑定到问号。在这里,它实际上会重复查询四次,对向量中的每个值执行一次。这样做可能看起来有点傻,但是当尝试传递字符串时,在许多方面让绑定负责设置适当的引用结构而不是尝试将其粘贴到自己中要安全得多。这样会产生更少的错误并避免很多数据库安全问题。
【讨论】:
我会试试的。当使用 c() 时,返回一个关于未使用的“cond”的错误,并且只使用了第一个值......但也许 list() 是魔法。我在github上找到了代码,没有看到任何明显的东西。在我的情况下,执行 4 条语句可能没问题。 严格来说,sqkExecute
想要一个数据框。列表恰好很容易转换为数据框。此外,您不会注意到它进行了四次调用。 sqlExecute
循环遍历数据框的行,为每一行提取一个数据框,并在最后将它们绑定在一起。在 R 端,无论 SQL 端发生什么,它都会感觉像是一次调用。
是的 - 谢谢。当我重新创建我的样本时,这是一个错字。我正在使用 data.frame ... data.frame(names=c("john",....) 这当然没有用。
嗨@Benjamin - 抱歉耽搁了,真正的工作妨碍了。感谢您使用 list() 提供的帮助和提示。我使用了您的示例,发现它确实发出了多个查询!但是,该实现使用游标来完成工作,并且整体 cpu/reads/duration 比在 SQL 中发出本机 IN 语句要长。在我使用数据子集的特定情况下,开销很大(980 vs 296 ms 和 5200 vs 126 读取)。随着我的全套数据的增长,我担心可扩展性。不过谢谢你 - 了解了一些我不知道的图书馆。【参考方案2】:
如果您在字符对象中声明一个变量表,然后与查询连接会怎样。
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)
【讨论】:
感谢您的想法。但是用值简单地输出 IN 子句更容易。临时表不会为小列表购买任何东西,但会通过网络添加要编译的文本。我想避免 sqlinjection 问题并获得编译后的可重用查询计划。生成临时表代码会导致生成不同的查询,因此无法重用计划。以上是关于R RODBCext 和参数化 IN 语句?的主要内容,如果未能解决你的问题,请参考以下文章
使用 MySql、PHP 和 ADODB 在准备好的语句中参数化 IN 子句
SQL Server参数化SQL语句中的like和in查询的语法(C#)
SQL Server参数化SQL语句中的like和in查询的语法(C#)
SQL Server参数化SQL语句中的like和in查询的语法(C#)