R DBI ODBC错误:nanodbc / nanodbc.cpp:3110:07009:[Microsoft] [SQL Server的ODBC驱动程序13]无效的描述符索引

时间:2017-07-09 21:23:35

标签: r sql-server odbc r-dbi nanodbc

我继续阅读DBI/ODBCRODBC更快,所以我尝试如下:

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:310:07009:[Microsoft] [SQL Server的ODBC驱动程序13]无效的描述符索引

我做错了什么? 请不要RODBC解决方案。 谢谢!

7 个答案:

答案 0 :(得分:4)

有一种解决方法:

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

如果您有dbply本身生成的复杂查询,请直接通过show_query()获取SQL查询。复制粘贴并修改第一个SELECT语句,使得长数据类型在列表中排在最后。它应该可以工作。

编辑: 在许多情况下,可以通过添加

来重新排序字段
%>% select(var1, var2, textvar1, textvar2)

查询。

答案 1 :(得分:3)

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

答案 2 :(得分: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)
}

答案 3 :(得分:1)

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

答案 4 :(得分: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))

答案 5 :(得分:0)

我很高兴很久以前就问过这个问题,但是我设法找到了解决方法。上面的答案使我了解了大部分情况。我遇到的问题是在模式表为-1的nvarchar类型的列中具有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)

答案 6 :(得分:-1)

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

简而言之,当某些文本列没有出现在整数/数字列之后时,就会发生此问题。当查询中的列未正确对齐时,将引发错误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)))

关于发生这种情况的原因,我不确定,并且我没有数据访问权限可以在用例中进行更深入的研究