17

我继续阅读DBI/ODBCis faster than RODBC,所以我尝试如下:

require(DBI);require(odbc)
con <- DBI::dbConnect(odbc::odbc(), dsn = 'SQLSERVER1', database = 'AcumaticaDB')

我可以成功连接到 DSN,但查询如下:

rs <- dbGetQuery(con, "SELECT * FROM inventoryitem")
dbFetch(rs)

给我以下错误:

result_fetch(res@ptr, n, ...) 中的错误:nanodbc/nanodbc.cpp:3110:07009:[Microsoft][ODBC Driver 13 for SQL Server]Invalid Descriptor Index

我究竟做错了什么 ?请,没有RODBC解决方案。谢谢!

4

9 回答 9

12

几个月来,我也一直在努力解决这个问题。但是,我遇到了一个可能对您也有帮助的解决方案。

简而言之,当某些文本列未出现在整数/数字列之后时,就会出现此问题。当查询中的列未正确对齐时,invalid index会引发错误并且您的连接可能会冻结。那么问题是,我怎么知道在我的查询末尾放什么?

要确定这一点,通常可以使用class()或检查列typeof()。要检查数据库中的此类信息,您可以使用如下查询:

dbColumnInfo(dbSendQuery(con, "SELECT * from schema.table")) # You may not require the schema part...

这将为感兴趣的数据集中的每一列返回一个带有类型字段的表。然后,您可以使用此表作为索引来对select()语句进行排序。我特别的困难是type表中的字段都是数字!但是,我注意到每个带有负数的列,当放在 select 语句的末尾时,修复了我的查询,我可以很好地拉出整个表。例如,我的完整解决方案

# Create my index of column types (ref to the current order)
index <- dbColumnInfo(dbSendQuery(con, "SELECT * from schema.table"))
index$type <- as.integer(index$type) # B/c they are + and - numbers!

# Create the ref to the table
mySQLTbl <- tbl(con, in_schema("schema", "tablename"))

# Use the select statement to put all the + numbered columns first!
mySQLTbl %>%
  select(c(which(index$type>=0),
                 which(index$type<0)))

至于发生这种情况的原因,我不确定,而且我没有数据访问权限来更深入地研究我的用例

于 2019-03-18T21:40:45.193 回答
9

我很感激很久以前就有人问过这个问题,但是我设法找到了解决方法。上面的答案让我大部分时间都在那里。我遇到的问题是 nvarchar 类型的列在 -1 的模式表中具有 CHARACTER_MAXIMUM_LENGTH,我理解这意味着它们是可能的最大长度。

我的解决方案是在 INFORMATION_SCHEMA.COLUMNS 表中查找相关表,然后适当地重新排列我的字段:

require(DBI);require(odbc)
library(tidyverse)
con <- DBI::dbConnect(odbc::odbc(), dsn = 'SQLSERVER1', database = 'AcumaticaDB')

column.types <- dbGetQuery(con, "SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='inventoryitem'")

ct <- column.types %>%
  mutate(cml = case_when(
    is.na(CHARACTER_MAXIMUM_LENGTH) ~ 10,
    CHARACTER_MAXIMUM_LENGTH == -1 ~ 100000,
    TRUE ~ as.double(CHARACTER_MAXIMUM_LENGTH)
    )
  ) %>%
  arrange(cml) %>%
  pull(COLUMN_NAME)

fields <- paste(ct, collapse=", ")
query <- paste("SELECT", fields, "FROM inventoryitems")

tbl(con, sql(query)) %>% head(5)
于 2019-12-02T22:26:03.673 回答
7

有一个解决方法:

重新排序您的SELECT语句,以使较长的数据类型(通常是字符串)在最后。

如果你有一个dbplyr自己生成的复杂查询,那么直接通过show_query(). 复制粘贴并修改第一SELECT条语句,使长数据类型位于列表的最后。然后它应该工作。

编辑:在许多情况下,可以通过将其添加到查询来重新排序字段:

%>% select(var1, var2, textvar1, textvar2)
于 2018-01-17T13:18:24.523 回答
6
rs <- dbGetQuery(con, "SELECT * FROM inventoryitem")
dbFetch(rs)

如果inventoryitem表包含长数据/可变长度列(例如VARBINARYVARCHAR)和简单类型列(例如INT)的混合,您不能通过 ODBC 以任意顺序查询它们。

应用程序应确保将长数据列放在选择列表的末尾。

长数据使用 ODBC API 调用从数据库中检索,SQLGetData并且必须在获取行中的其他数据后检索。

这些是已知并记录在案的 ODBC 限制

要从列中检索长数据,应用程序首先调用 SQLFetchScroll 或 SQLFetch 以移动到行并获取绑定列的数据。然后应用程序调用 SQLGetData。

请参阅https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/getting-long-data

于 2017-09-10T23:24:16.390 回答
1

我最近确实遇到了这个问题。这是我的解决方案。基本上,您必须首先根据从数据库中获取的列信息对列进行重新排序。列可以混合正负类型。所以先用正数对它们进行排序,然后用负数排序就可以了。

当出现“无效的描述符索引”问题时,它与我的数据完美配合。请让我知道它是否也适合您。

sqlFetchData <- function(connection, database, schema, table, nobs = 'All') {

  #'wrap function to fetch data from SQL Server
  #
  #@ connection: an established odbc connection
  #@ database: database name
  #@ schema: a schema under the main database
  #@ table: the name of the data table to be fetched. 
  #@ nobs: number of observation to be fetched. Either 'All' or an integer number. 
  #        The default value is 'All'. It also supports the input of 'all', 'ALL' and
  #        etc. . 

  if (is.character(nobs)) {
    if (toupper(nobs) == 'ALL') {
      obs_text <- 'select'
    } else {
      stop("nobs could either be 'ALL' or a scalar integer number")
    }
  } else {
    if (is.integer(nobs) && length(nobs) == 1) {
      obs_text <- paste('select top ', nobs, sep = '')
    } else {
      stop("nobs could either be 'ALL' or a scalar integer number")
    }
  }

  initial_sql <- paste("select * from ", database, '.', schema, ".", table, 
                       sep = '')
  dbquery <- dbSendQuery(connection, initial_sql)
  cols <- dbColumnInfo(dbquery) 
  dbClearResult(dbquery)

  #' sort the rows by query type due to error message:
  #' Invalid Descriptor Index 

  colInfo <- cols
  colInfo$type <- as.integer(colInfo$type)
  cols_neg <- colInfo[which(colInfo$type < 0), ]
  cols_neg <- cols_neg[order(cols_neg[, 2]), ]
  cols_pos <- colInfo[which(colInfo$type >= 0), ]
  cols_pos <- cols_pos[order(cols_pos[, 2]), ]
  cols <- rbind(cols_pos, cols_neg)

  add_comma <- "c(cols$name[1], paste(',', cols$name[-1L], sep = ''))"

  sql1 <- paste(c(obs_text, eval(parse(text = add_comma))),
                collapse = ' ', sep = '')
  data_sql <- paste(sql1, ' from ', database, '.', schema, '.', table, 
                    sep = '')

  dataFetch <- dbGetQuery(connection, data_sql)[, colInfo$name]
  return(dataFetch)
}
于 2020-04-07T05:51:00.863 回答
1

ODBC/DBI 在建立连接时将数据库中的字符变量数据类型转换为“ntext”。因此,您需要将 R 中 SQL 字符串中的字符变量(例如 x)转换为 CONVERT(varchar(100),x)。然后 dbGetQuery 函数应该可以工作。

于 2020-07-01T17:49:06.767 回答
0

也许我错过了其他人的添加,但是我遇到了完全相同的错误,通读了这个字符串并尝试了一些复杂的事情,比如重新排序列,并且几个月来不知道如何解决它。然后,我发现我只需升级到适用于我的 Windows 10 的最新 SQL Server 驱动程序并更改连接字符串中的驱动程序,问题就解决了。

于 2022-01-21T00:31:31.647 回答
0

由于尝试加载时间戳变量,我得到了这个错误。尝试从查询中删除任何时间戳变量。

试试下面的或类似的。让我知道什么有效,我会更新我的帖子。

require(DBI);require(odbc)
con <- DBI::dbConnect(odbc::odbc(), dsn = 'SQLSERVER1', database = 'AcumaticaDB')

column.types = DBI::dbGetQuery( 
    con, 
    'SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = "inventoryitem"' 
))

sql = paste(c(
        'select ', 
        paste(column.types$COLUMN_NAME[column.types$DATA_TYPE != 'timestamp'], collapse = ', '), 
        ' from inventoryitem'
     ),
    collapse = ''
)

dbFetch(dbGetQuery(con, sql))
于 2018-12-28T22:28:57.427 回答
0

如果您有一个类型为 的字段,也会发生此错误geometry。在拉入 data.table 之前,您应该SELECT AS并使用空间方法转换为 WKT。见下文,您知道这Shape是一个 type 字段geometry。通过使用空间方法.STAsText(),它将其转换为众所周知的文本 (WKT) 格式,R 可以更轻松地使用该格式。

library(sf)
library(tidyverse)

query <- "
SELECT company, revenue, address, Shape.STAsText() AS ShapeWKT
FROM businesses_table
"

results <- dbFetch(dbSendQuery(con, query)) %>%
  filter(!is.na(ShapeWKT)) %>%
  mutate(Shape = st_as_sfc(ShapeWKT)) %>%
  select(-ShapeWKT) %>%
  st_as_sf()

我构建了一个更复杂的函数,允许您指定数据库连接、表名、字段异常(默认返回除字段中的字段外的所有字段exception)、用于过滤查询的 WHERE 子句,并指定空间字段名称,因为它因表而异(即,可能是Shape可能是Geo等)。

spatial_query <- function(con, tablename, 
                          exception = "", 
                          wherefilter = "", 
                          spatialfieldname = "Shape",
                          spatial = TRUE){
  ## Get last element (table name) from explicit table name
  tmp <- strsplit(tablename, "[.]")[[1]]
  tablename_short <- tmp[length(tmp)]
  
  fields <- dbListFields(con, tablename_short)
  
  if(spatial){
    fields_to_grab <- fields[!(fields %in% c(exception, spatialfieldname))]
    ## TODO adjust query to be responsive to Shape
    qstring <- paste0('SELECT ', paste(fields_to_grab, collapse = ", "), ', ', spatialfieldname, '.STAsText() AS ShapeWKT FROM ', tablename, ' ', wherefilter)
    cat(paste("Sending following SQL statement:", qstring, "\n", sep = "\n"))
    ret <- dbFetch(dbSendQuery(con, qstring)) %>%
      filter(!is.na(ShapeWKT)) %>%
      mutate(Shape = st_as_sfc(ShapeWKT)) %>%
      select(-ShapeWKT) %>%
      st_as_sf()
  }
  
  else{
    fields_to_grab <- fields[!(fields %in% exception)]
    qstring <- paste0('SELECT ', paste(fields_to_grab, collapse = ", "),' FROM ', tablename, ' ', wherefilter)
    cat(paste("Sending following SQL statement:", qstring, "\n", sep = "\n"))
    ret <- dbFetch(dbSendQuery(con, qstring))
  }
  
  return(ret)
}
于 2021-11-24T19:04:36.197 回答