【问题标题】:RODBC: sqlUpdate() doesn't recognise index columnRODBC:sqlUpdate() 无法识别索引列
【发布时间】:2012-08-10 12:48:05
【问题描述】:

我的数据库表大概是这样的:

+-----+-------+--------------------+-----------+----------+
| ID1 | ID2   | FilePath1          | FilePath2 | Status   |
+-----+-------+--------------------+-----------+----------+
| 1   | Test1 | MyFolder\R\Folder1 | NULL      | Open     |
| 2   | Test2 | MyFolder\R\Folder2 | NULL      | Open     |
| 3   | Test3 | MyFolder\R\Folder3 | NULL      | Finished |
| 4   | Test4 | MyFolder\R\Folder4 | NULL      | Finished |
+-----+-------+--------------------+-----------+----------+

第一列 (ID1) 定义为 PK。但是,ID2 也是唯一的。

现在我希望能够使用来自RODBC 包的sqlUpdate() 用R 更改FilePath2 和Status。所以我尝试以下方法:

db.df <- data.frame(ID1=1, ID2='Test1',
                    FilePath2='MyFolder\R\Folder5', Status='Finished',
                    stringsAsFactors=FALSE)

sqlUpdate(myconn, db.df, tablename='mytable', index='ID2', verbose=TRUE)

其中db.df 是一个数据框,其中一行和列名对应于数据库表中的行名(但是,我省略了一些列,在本例中为FilePath1,我更愿意省略ID1,如果可能的话)。我的目标是获得以下内容:

+-----+-------+--------------------+--------------------+----------+
| ID1 | ID2   | FilePath1          | FilePath2          | Status   |
+-----+-------+--------------------+--------------------+----------+
| 1   | Test1 | MyFolder\R\Folder1 | MyFolder\R\Folder5 | Finished |
| 2   | Test2 | MyFolder\R\Folder2 | NULL               | Open     |
| 3   | Test3 | MyFolder\R\Folder3 | NULL               | Finished |
| 4   | Test4 | MyFolder\R\Folder4 | NULL               | Finished |
+-----+-------+--------------------+--------------------+----------+

我收到以下错误:

Error in sqlUpdate(myconn, db.df, tablename = 'mytable', index = 'ID2',  : 
index column(s) ID2 not in database table

这个问题的原因可能是什么?


编辑:我通过发送直接 SQL 查询绕过了这个问题:

out.path <- 'MyFolder\\\\R\\\\Folder5'
update.query <- paste("UPDATE mytable ", 
                  "SET FilePath2='", out.path, "', Status='Finished' ",
                  "WHERE ID2='Test1'", sep="")
dummy <- sqlQuery(myconn, update.query)

虽然这可能不是一个简洁的方法,但它做了它应该做的事情。不过还是不明白sqlUpdate是怎么回事,希望有大神指点一下。

【问题讨论】:

    标签: sql r rodbc


    【解决方案1】:

    在某些情况下,尤其是当您传递多个列时,您需要将列结构显式指定为列名。

    示例:sqlUpdate(myconn, db.df, tablename='mytable', index=names('ID2'), verbose=TRUE)

    更新:所以有时它似乎仍然失败。我使用的新工作是:

    这允许您在需要时传递列列表。不知道为什么反其道而行之。

    indexNames

    sqlUpdate(myconn, db.df, tablename='mytable', index=as.character("ID2"), verbose=TRUE)

    【讨论】:

      【解决方案2】:

      我在使用sqlUpdate 更新 MySQL 中的表时遇到了类似的问题。我通过在 R-MySQL 连接中设置case 属性来修复它。

      详情如下:

      在 MySQL 中:

      create table myTable (
      myName1 INT NOT NULL PRIMARY KEY,
      myName2 VARCHAR(10) NOT NULL,
      );
      
      insert into myTable values(111, 'Test1')
      insert into myTable values(222, 'Test2')
      

      在 R 中:

      myDF <- data.frame(myName1 = 111, myName2 = 'Test3')
      sqlUpdate(myConn, myDF, tablename='myTable', index = 'myName1', verbose=TRUE)
      
      #> Error in sqlUpdate(myConn, myDF, tablename='myTable', index = 'myName1', verbose=TRUE) : 
        index column(s) myName1 not in data frame
      

      原因是 RMySQL 连接中的(默认?)属性有:

      > attr(myConn, "case")
      [1] "tolower"
      

      因此,myDF 中的 colname myName1 在 sqlUpdate 中的大小写更改为 myname1,因此它与给定索引的 myName1 不匹配。

      请注意,如果使用index = 'myname1' 更改呼叫,它将不起作用。会报错index column(s) myName1 not in database table。因为在 MySQL 表中,colname 是 myName。

      解决方法是在连接时或连接后将case属性设置为'nochange':

      attr(myConn, "case") <- 'nochange'
      

      这里有更多细节:

      debugonce(sqlUpdate) 给出:

         cnames <- colnames(dat)
          cnames <- mangleColNames(cnames)
          cnames <- switch(attr(channel, "case"), nochange = cnames, 
              toupper = toupper(cnames), tolower = tolower(cnames))
          cdata <- sqlColumns(channel, tablename)
          coldata <- cdata[c(4L, 5L, 7L, 9L)]
          if (is.character(index)) {
              intable <- index %in% coldata[, 1L]
              if (any(!intable)) 
                  stop("index column(s) ", paste(index[!intable], collapse = " "), 
                      " not in database table")
              intable <- index %in% cnames
              if (any(!intable)) 
                  stop("index column(s) ", paste(index[!intable], collapse = " "), 
                      " not in data frame")
              indexcols <- index
          }
      

      注意intable 对cname 和coldata 的调用。

      【讨论】:

        【解决方案3】:

        sqlUpdate 对我有用。我唯一需要改变的是 db.df - 需要将\ 字符加倍,这样它就不会试图用它来转义代码。我的测试表如下所示:

        CREATE TABLE mytable  (
            ID1 INT NOT NULL PRIMARY KEY,
            ID2 VARCHAR(10) NOT NULL,
            FilePath1 VARCHAR(50) NOT NULL,
            FilePath2 VARCHAR(50) NULL,
            Status VARCHAR(15) NOT NULL)
        
        insert into mytable values(1,'Test1','MyFolder\R\Folder1',NULL,'Open')
        insert into mytable values(2,'Test2','MyFolder\R\Folder2',NULL,'Open')
        insert into mytable values(3,'Test3','MyFolder\R\Folder3',NULL,'Finished')
        insert into mytable values(4,'Test4','MyFolder\R\Folder4',NULL,'Finished')
        

        我能够在更新中没有 ID1 或 FilePath1 字段的情况下运行更新。如果您阅读文档 (?sqlUpdate),它会指出:'首先它会查找表的主键,然后查找数据库认为最适合唯一定义行的列 ' 因此您不必使用主键,但如果您不知道 ID2 是唯一的,最好使用该主键。

        db.df <- data.frame(ID2='Test1', FilePath2='MyFolder\\R\\Folder5', Status='Finished',                    stringsAsFactors=FALSE)
        sqlUpdate(myconn, db.df, tablename='mytable', index='ID2', verbose=TRUE)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-03-08
          • 1970-01-01
          • 2012-01-11
          • 2021-06-05
          • 1970-01-01
          • 2018-08-22
          • 2018-10-14
          • 2020-12-06
          相关资源
          最近更新 更多