web-dev-qa-db-ja.com

R DBI ODBCエラー:nanodbc / nanodbc.cpp:3110:07009:[Microsoft] [SQLServer用ODBCドライバー13]無効な記述子インデックス

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:3110:07009:[Microsoft] [SQLServer用ODBCドライバー13]無効な記述子インデックス

私は何が間違っているのですか? RODBCソリューションは使用しないでください。ありがとう!

9
user2948714
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 を参照してください。

6
mloskot

私もこの問題に数ヶ月苦労しています。しかし、私はあなたにも役立つかもしれない解決策に出くわしました。

一言で言えば、この問題は、特定のテキスト列が整数/数値列の後に表示されない場合に発生します。クエリで列が適切に配置されていない場合、_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)))
_

これが発生する理由については、よくわかりません。また、ユースケースでさらに深く掘り下げるためのデータアクセス権限がありません。

6
al-obrien

回避策があります:

SELECTステートメントを並べ替えて、より長いデータ型(通常は文字列)が最後になるようにします。

dbply自体によって生成される複雑なクエリがある場合は、show_query()を介してSQLクエリを直接取得します。長いデータ型がリストの最後になるように、最初のSELECTステートメントをコピーして貼り付けて変更します。その後、動作するはずです。

編集:多くの場合、追加することでフィールドを並べ替えることができます

%>% select(var1, var2, textvar1, textvar2)

クエリに。

4
mzuba

この質問がずっと前に行われたことを感謝しますが、回避策を見つけることができました。上記の答えは私をそこへの道のほとんどに導いた。私が抱えていた問題は、スキーマテーブルに-1のCHARACTER_MAXIMUM_LENGTHを持つnvarchar型の列にありました。これは、それらが可能な最大長であることを意味すると理解しています。

私の解決策は、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)
1
tricky999

私は確かに最近この問題に遭遇しました。これが私の解決策です。基本的に、最初にデータベースからフェッチされた列情報に基づいて列を並べ替える必要があります。列は、正および負のタイプと混合する可能性があります。したがって、最初に正でソートし、次に負でソートするとうまくいきます。

「無効な記述子インデックス」の問題がある場合、データと完全に連携します。それがあなたにも役立つかどうか教えてください。

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)
}
0
Fred

タイムスタンプ変数を読み込もうとした結果、このエラーが発生しました。クエリからタイムスタンプ変数を削除してみてください。

以下または同様のものを試してください。何が機能するか教えてください。投稿を更新します。

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))
0