3

我无法让 R 语言 DBI::sqlAppendTable 函数处理数字以外的任何内容。下面是一些说明问题的代码。我怀疑问题是 sqlAppendTable 没有引用数据。任何修复或解决方法将不胜感激。

num = data.frame(matrix(1:26, ncol=2))
let = data.frame(matrix(letters, ncol=2))

test.sqlAppendTable = function(dfr) {
    #dfr: A data frame.
    conx <- dbConnect(RSQLite::SQLite(), ":memory:")
    on.exit(dbDisconnect(conx))
    dbWriteTable(conx, "temp", dfr[1:5, ])
    temp = dbReadTable(conx, 'temp')
    print(temp)
    sat = sqlAppendTable(conx, 'temp', dfr[6:10, ])
    print(sat)
    rs = dbExecute(conx, sat)
    cat('Result set (rs): ')
    print(rs)
    temp = dbReadTable(conx, 'temp')
    print(temp)
}

test.sqlAppendTable(num) #Runs fine.
test.sqlAppendTable(let) #Generates error:
#Error in rsqlite_send_query(conn@ptr, statement) : no such column: j
4

3 回答 3

2

我已经多次遇到这个问题,以至于没有尝试编写自己的解决方法。就个人而言,我在使用 Microsoft SQL Server 时遇到了同样的问题,但我认为同样的解决方案也适用于 SQLite。我正在与:

  • 数据库:托管在 Azure 中的 Microsoft SQL Server
  • R:3.5.0
  • DBI:1.0.0
  • 数据库:1.1.6
  • 操作系统:Ubuntu 18.04

方法:

为了效率,我想避免遍历行。我发现了这一点,mapply并且paste0可以以更面向列的方式进行组合。

我承认这有点“hacky”,但它对我自己来说效果很好。使用风险自负;我仅将其用于小型项目,而不是企业解决方案。无论如何,效率应该不是那么大的问题,因为无论如何插入都有1000 行的限制

替换“sqlAppendTable”:

db_sql_append_table <- function(p_df, p_tbl) {
    # p_df: data.frame that contains the data to append/insert into the table
    # the names must be the same as those in the database
    # p_tbl: the name of the database table to insert/append into
    
    num_rows <- nrow(p_df)
    num_cols <- ncol(p_df)
    requires_quotes <- sapply(p_df, class) %in% c("character", "factor", "Date")
    commas <- rep(", ", num_rows)
    quotes <- rep("'", num_rows)
    
    str_columns <- ' ('
    column_names <- names(p_df)
    
    for(i in 1:num_cols) {
        if(i < num_cols) {
            str_columns <- paste0(str_columns, column_names[i], ", ")
        } else {
            str_columns <- paste0(str_columns, column_names[i], ") ")
        }
    }
    
    str_query <- paste0("INSERT INTO ", p_tbl, str_columns, "\nVALUES\n")   
    str_values <- rep("(", num_rows)
    
    for(i in 1:num_cols) {
        
        # not the last column; follow up with a comma
        if(i < num_cols) {
            if(requires_quotes[i]) {
                str_values <- mapply(paste0, str_values, quotes, p_df[[column_names[i]]], quotes, commas)        
            } else {
                str_values <- mapply(paste0, str_values, p_df[[column_names[i]]], commas)
            }
            
        # this is the last column; follow up with closing parenthesis
        } else {
            if(requires_quotes[i]) {
                str_values <- mapply(paste0, str_values, quotes, p_df[[column_names[i]]], quotes, ")")
            } else {
                str_values <- mapply(paste0, str_values, p_df[[column_names[i]]], ")")
            }
        }
    }
    
    # build out the query; collapse values with comma & newline; end with semicolon;
    str_values <- paste0(str_values, collapse=",\n")
    str_query <- paste0(str_query, str_values)
    str_query <- paste0(str_query, ";")
    return(str_query)
}

调用函数:

我想让它尽可能地与原始sqlAppendTable功能相似。此函数仅构造查询。

您仍然必须将此函数包装在dbExecute()对数据库的实际插入/追加行的调用中。

dbExecute(conn=conn, statement = db_sql_append_table(my_dataframe, "table_name"))

编辑

  • 添加了“日期”作为该函数需要引用的类型之一。谢谢你的评论!
于 2018-08-26T21:32:08.150 回答
0

我已经能够拼凑出一个解决方法:

1) 将要附加的数据框作为临时表 (temp) 写入 SQLite 数据库文件。

2)使用SQLite语句将其附加到目标表(target):

insert into target select * from temp;

3)降低温度。

这运行得相当快,大概是由于 SQLite 得到了很好的优化。

附录:

您确实可以使用 dbWriteTable 将数据框附加到数据库表中,并带有选项 append=TRUE。我已经针对上述解决方法对此进行了测试,令人惊讶的是,该解决方法的运行速度比 dbWriteTable 快了近 40%。

于 2017-04-02T22:45:07.433 回答
0

这看起来像是RSQLite包装中的一个缺陷;确实应该引用值。请注意,在以下示例中添加了引号:

DBI::sqlAppendTable(DBI::ANSI(), table = "test", data.frame(a = 1, b = "2"))
#> <SQL> INSERT INTO "test"
#>   ("a", "b")
#> VALUES
#>   (1, '2')

另请参阅相应的GitHub 问题

于 2017-04-01T19:47:35.820 回答