将data.table upsert到SQL数据库中?

时间:2015-04-09 14:19:53

标签: sql r data.table

将R data.table实例更新/插入SQL数据库(例如MySQL)的最简单方法是什么?我有一个data.table的实例,其中配置了一些关键列(通过setkey)。现在,每当表的主键列与表的键匹配时(或者我可以手动映射),我想update对应的SQL表,但是对于那些键不匹配的行,insert他们

最好我自己不需要为它编写SQL。一个原因是数据列数量相对较多。另一个原因是我想要数据库独立性(主要是在MySQL,PostgreSQL和SQLite之间轻松切换)。

2 个答案:

答案 0 :(得分:1)

由于数据库中不同语法的变幻莫测,我认为很难避免使用特定于db的代码。

UPSERT

ANSI标准术语是MERGE和(根据wikipedia)大多数大型商业数据库都支持ANSI标准。由于语法的相对新近度被添加到标准中,因此有许多非标准实现在数据库中具有不同的语法,您希望它可以移植。

R

中的数据库访问

DBI

dplyr等人完成的大部分数据库I / O.是通过包DBI。 DBI包有一个INSERT函数dbWriteTable(),但文档很少。

RODBC

RODBC包具有sqlUpdate()功能,但它存在许多问题:

  1. 文档未提及记录的处理,即INSERT
  2. 没有粒度,即所有值都会更新,因此无法引入部分UPSERT
  3. Saves using RODBC可能会导致R会话崩溃。
  4. 话虽如此 - 它可以适用于您的场景。如果没有,我将在下面展示如何构建UPSERT语句并使用RODBC以paramaterized方式执行它。它不是可移植的代码,但它是动态的,只需要很少的努力就可以传递列而不进行更新。

    字符串构造

    mysql中的默认UPSERT语法是INSERT INTO ... ON DUPLICATE KEY UPDATE ...。这对于使用data.table的列名动态进行字符串构造非常容易。以下是为任何

    执行参数化UPSERT的示例函数
    library(data.table)
    library(RODBC)
    library(RODBCext)
    
    irisDT<- data.table(iris, key="Species")
    
    UPSERT<-function(DT, connectionString, destTable){
     sql<- paste0("INSERT INTO ",destTable," (",paste(colnames(DT ),
                                             collapse=","),")\n",
    # This bit inserts the parameterised bit i.e. where your values will go
                "VALUES (", paste(rep("?",ncol(DT )),
                                  collapse=",",sep=",") ,")\n",
                 "ON DUPLICATE KEY UPDATE \n",
    # This specifies on dup. key behaviour - doesn't include key
                 paste(colnames(DT )[!(colnames(DT ) %in% key(DT ))],
                 "=VALUE(",
                 colnames(DT )[!(colnames(DT ) %in% key(DT ))],
                 ")", collapse=",\n", sep="")
    
                  )
    
    RODBCext::sqlExecute(channel=connectionString, 
                         query=sql, 
                         data=DT
                         )
        }
    
    UPSERT(irisDT)
    

    您可能需要略微调整字符串结构,因为我的MySQL语法可能有些偏差。

    NB确保你在data.table上设置你的密钥在这里非常重要,否则你不能做一个UPSERT,除非你修改代码以排除特定的列但是我想要一个动态的解决方案对你而言。

    多步骤过程

    @hadley在评论中表示。可以进行多步骤不可知过程,即

    1. 从表格中获取现有值
    2. 识别新行并将其插入
    3. 非新行,通过更新声明发送
    4. 这将更加冗长,并且取决于新记录插入数据库表的速度可能会落在唯一性的约束和插入表中的新记录中,而不是插入一个重复的行。

答案 1 :(得分:1)

以下是修改sqlAppendTable所做查询的另一种解决方案。

sql_query <- sqlAppendTable(con, "table", DT)

sql_query@.Data <- paste0(sql_query@.Data, "\n  ","ON DUPLICATE KEY UPDATE col1 = values(col1), col2 = values(col2)")

dbSendQuery(con, sql_query)

更改col1col2以了解应更新的列。根据需要添加其他人。可能您可以先确定主键并将其转换为通用函数。