2

我正在尝试使用 ROracle 在 R 中处理大量带有参数的查询。我知道每个查询中出现了哪些参数,但我不知道它们出现的顺序。因此,我正在寻找一种在每个查询中按名称提交参数的方法。示例代码:

library(ROracle)

# establish connection to DB
drv <- dbDriver("Oracle")
con <- dbConnect(drv, "User", "password", dbname = "DB")

# create table
createTab <- "create table RORACLE_TEST(num1 number, num2 number)"
dbGetQuery(con, createTab)

# insert String
insStr <- "insert into RORACLE_TEST values(:row1, :row2)"
dbGetQuery(con, insStr, data.frame(row2 = 0, row1 = 1))

# check output
dbGetQuery(con, "SELECT * FROM RORACLE_TEST")

# Output is:
#  NUM1 NUM2
#1    0    1

# Desired output should be:
#  NUM1 NUM2
#1    1    0

除此类解决方案外,任何解决此问题的方法都将不胜感激

dbGetQuery(con,gsub(":row2", "0", gsub(":row1", "1", insStr)))

因为这不会对 sql 注入进行清理(参数将来自用户输入)。

4

2 回答 2

1

我最近自己在同一个问题上花了一些时间,但没有找到完美的解决方案。在我看来,从语法的角度来看,使用命名占位符是非常错误的,因为它给人的印象是参数顺序无关紧要。

我相信这不是图书馆的问题,也不是ROracle图书馆的责任,因为这也会导致一个结果,对于不了解 PL/SQL 的人来说,这是不期望的:

DEFINE
  row1 number;
  row2 number;
BEGIN
  row1 := 1;
  row2 := 0;
  EXECUTE IMMEDIATE 'insert into RORACLE_TEST values(:row1, :row2)' USING row2, row1;
END;
/

当我们将应用程序从RODBCwith迁移RODBCext到 时ROracle,我们一直使用?作为绑定变量占位符,并在我们的数据库连接 API 中将它们替换为冒号样式。至少这不应该引起任何人的注意:

# placeholders '?' are replaced with :1 and :2 in custom_dbGetQuery()
insStr <- "insert into RORACLE_TEST values(?, ?)"
custom_dbGetQuery(con, insStr, data.frame(row2 = 0, row1 = 1))

编辑:添加了重新排序数据框的建议

您可以通过检查查询字符串中出现的占位符自己重新排序数据框来进一步:

custom_dbGetQuery <- function(con, insStr, data) {
    names <- names(data)

    name.pos <- sort(sapply(names, function(ph) { 
        regexp <- paste0(":", ph, "[^\\w]")
        matches <- gregexpr(regexp, insStr, perl = TRUE)
        matches <- unlist(matches)
        stopifnot(length(matches) == 1, all(matches != -1))
        matches 
    }))

    data <- data[, names(name.pos)]

    print("running query:")
    print(insStr)
    print("using data:")
    print(data)

    dbGetQuery(con, insStr, data)
}

insStr <- "insert into RORACLE_TEST values(:row1, :row2)"
custom_dbGetQuery(con, insStr, data.frame(row2 = 0, row1 = 1))

# Output:
# [1] "running query:"
# [1] "insert into RORACLE_TEST values(:row1, :row2)"
# [1] "using data:"
#   row1 row2
# 1    1    0
于 2018-04-20T12:17:18.100 回答
0

受(感谢@Scarabee!)tidyverse启发的解决方案:sqlInterpolate

readr::read_file('query.sql') %>%
stringr::str_replace_all(., ':','?') %>%
DBI::sqlInterpolate(con, ., row2 = 0, row1 = 1) %>%
DBI::dbGetQuery(con, .)
于 2018-10-22T01:09:05.347 回答