【问题标题】:RODBC and Microsoft SQL Server: Truncating Long Character StringsRODBC 和 Microsoft SQL Server:截断长字符串
【发布时间】:2017-10-30 04:38:47
【问题描述】:

我正在尝试使用 R/RODBC 从 Microsoft SQL Server 数据库中查询一个变量。 RODBC 将字符串截断为 8000 个字符。

原始代码:截断 255 个字符(根据 RODBC 文档) library(RODBC) con_string <- odbcConnect("DSN") query_string <- "SELECT text_var FROM table_name" dat <- sqlQuery(con_string, query_string, stringsAsFactors=FALSE)

部分解决方案:修改查询字符串在 7999 个字符后截断文本。 library(RODBC) con_string <- odbcConnect("DSN") query_string <- "SELECT [text_var]=CAST(text_var AS VARCHAR(8000)) FROM table_name" dat <- sqlQuery(con_string, query_string, stringsAsFactors=FALSE)

表格/变量包含长达 250,000 个字符的文本字符串。我真的很想处理 R 中的所有文本。这可能吗?

@BrianRipley 在以下文档的第 18 页讨论了问题(但没有解决方案): https://cran.r-project.org/web/packages/RODBC/vignettes/RODBC.pdf

@nutterb 在 GitHub 上讨论了与 RODBCext 包类似的问题: https://github.com/zozlak/RODBCext/issues/6

已经看到关于 SO 的类似讨论,但没有使用带有 VARCHAR>8000 的 RODBC 的解决方案。

RODBC sqlQuery() returns varchar(255) when it should return varchar(MAX)

RODBC string getting truncated

注意:

  • R 3.3.2
  • Microsoft SQL Server 2012
  • Linux RHEL 7.1
  • 用于 SQL Server 的 Microsoft ODBC 驱动程序

【问题讨论】:

  • 关于部分解决方案 - 您可能想知道为什么它会在 7999 而不是 8000 处截断。这是由于 RODBC 中的错误已在 RODBCext 包中修复 - 请特别参阅 github.com/zozlak/RODBCext/issues/8。此外,在 RODBCext 中,zozlak 已做出设计选择,如果从 VARCHAR(max) 类型的列中提取,则默认情况下允许 65,535 个字符。因此,对于未来的读者,我会推荐类似于@Benjamin 下面的方法的方法——但以 65,535 而不是 8,000 的增量循环。

标签: sql-server r truncate rhel rodbc


【解决方案1】:

由于这是 Microsoft 提供的 ODBC 驱动程序的一个限制,因此在他们对驱动程序进行更改之前几乎没有什么可做的。 @zozlak 在您链接到的 GitHub 问题中解释了原因。

我倾向于在需要时使用存储过程来解决这个问题,但这通常需要为每个特定实例编写一个存储过程。在某些时候,我可能会想出一种更通用的在存储过程中执行此操作的方法,但我发现在存储过程中构造查询的过程既乏味又令人沮丧。

出于这个原因,我只是花了一些时间来构建一个函数,该函数将执行涉及 VARCHAR(MAX) 变量的有限查询。这是一种蛮力方法,对于 17000 个字符的变量,会将其导出为三个变量并将它们粘贴到 R 中。它很粗糙,可能不是很有效,但我想出的最佳解决方案。

另一个限制是它不允许您在查询中重命名变量。您将被数据库中命名的变量所困扰。如果您只涉及几张桌子,那可能不是问题。在非常复杂的数据库中,这可能是有问题的。但是,至少有了这个,您可以只查询具有少数必要 ID 的 VARCHAR(MAX) 变量,以便在 R 中执行合并。

正如 GitHub 问题中所讨论的,最好尽可能避免使用 VARCHAR(MAX)。如果确实需要未知长度,则 VARBINARY(MAX) 更容易查询。

示例

source("https://gist.githubusercontent.com/nutterb/d2e050dada608bb6213e61d0f8471b65/raw/be8717f318b3e3087e7c26c9a8f9d0a582a5daef/query_varchar_max"

channel <- odbcDriverConnect(...)

query_varchar_max(channel = channel,
                  id = c("idvar"),
                  varchar_max = c("varchar_max_var", "varchar_max_var2"),
                  from = "FROM dbo.table_name WHERE group = ?",
                  data = list(group = "A"))

功能代码

#' @name query_varchar_max
#' @title Query a VARCHAR(MAX) Variable from SQL Server
#' 
#' @description The RODBC driver to SQL Server (SQL Server Native Client 11.0)
#'   reports the lenght of a VARCHAR(MAX) variable to be zero.  This presents 
#'   difficulties in extracting long text values from the database. Often, the
#'   ODBC will assume a length of 255 characters and truncate the text to that
#'   many characters.  The approach taken here searches the VARCHAR(MAX) variables 
#'   for the longest length, and extracts the data in segments to be pasted 
#'   together in R.  
#'   
#' @param channel A valid ODBC channel to a SQL Server database.
#' @param id A character vector of ID variables that may be used to merge the 
#'   data from this query into another dataset.
#' @param varchar_max a character vector of variable names that are to be 
#'   treated as if they are VARCHAR(MAX) variables. 
#' @param from A single character string providing the remainder of the query 
#'   to be run, beginning with the \code{FROM} statement.
#' @param stringsAsFactors \code{logical(1)}. Should character strings returned 
#'   from the database be converted to factors?
#' @param ... Additional arguments to \code{sqlExecute} when running the full 
#'   query.
#'   
#' @details \code{query_varchar_max} operates by determining how many columns of up to
#'   8000 characters each are required to export a complete VARCHAR(MAX) variable.
#'   It then creates the necessary number of intermediate variables and queries the 
#'   data using the SQL Server \code{SUBSTRING} command, extracting the VARCHAR(MAX)
#'   variable in increments of 8000 characters. After completing the query, 
#'   the intemediary variables are concatenated and removed from the data.
#'   
#'   The function makes accommodation for multi-part queries as far as [TABLE].[VARIABLE]
#'   formats are concerned. It is not intended for use in [SCHEMA].[TABLE].[VARIABLE]
#'   formats. This at least allows \code{from} to include joins for more complex 
#'   queries.  Parameterized queries are also supported through \code{sqlExecute}.
#'
#' @export

query_varchar_max <- function(channel, id, varchar_max, from, 
                              stringsAsFactors = FALSE, ...)
{
  coll <- checkmate::makeAssertCollection()

  checkmate::assert_class(x = channel,
                          classes = "RODBC",
                          add = coll)

  checkmate::assert_character(x = id,
                              add = coll)

  checkmate::assert_character(x = varchar_max,
                              add = coll)

  checkmate::assert_character(x = from,
                              len = 1,
                              add = coll)

  checkmate::assert_logical(x = stringsAsFactors,
                            len = 1,
                            add = coll)

  checkmate::reportAssertions(coll)

  varchar_max_len <-
    paste0(
      sprintf("MAX(LEN(%s)) AS len_%s", 
              varchar_max,
              sub("[.]", "_", varchar_max)),
      collapse = ", "
    )

  varchar_len <- 
    unlist(
      RODBCext::sqlExecute(
        channel = channel,
        query = sprintf("SELECT %s %s",
                        varchar_max_len,
                        from),
        fetch = TRUE
      )
    )

  varchar_max_cols <- 
    unlist(
      mapply(expand_varchar_max,
             varchar_max,
             varchar_len,
             SIMPLIFY = FALSE)
    )

  Prelim <- 
    RODBCext::sqlExecute(
      channel = channel,
      query = sprintf("SELECT %s, %s %s",
                      paste0(id, collapse = ", "), 
                      paste0(varchar_max_cols, collapse = ", "),
                      from),
      fetch = TRUE,
      stringsAsFactors = stringsAsFactors,
      ...
    )

  var_stub_to_combine <-
    unique(
      sub(
        "(part)(\\d{1,3})", 
        "\\1",
        sub(".+AS ", "", varchar_max_cols)
      )
    )

  col_to_combine <- 
    lapply(var_stub_to_combine,
           grep,
           names(Prelim))

  Prelim[sub(".+[.]", "", varchar_max)] <-
    lapply(col_to_combine,
           function(col) apply(Prelim[col], 1, paste0, collapse = ""))

  Prelim[-unlist(col_to_combine)]

}


expand_varchar_max <- function(varchar_max, varchar_len)
{
  nvar <- varchar_len %/% 8000 + 1

  var_list <- vector("character", length = nvar)

  for (i in seq_along(var_list))
  {
    var_list[i] <- 
      sprintf("SUBSTRING(%s, %s, %s) AS %s_part%s",
              varchar_max,
              1 + (i - 1) * 8000,
              8000,
              paste0(sub("[.]", "_", varchar_max)),
              i)
  }
  var_list
}

【讨论】:

    【解决方案2】:

    也许最近这种情况发生了变化,但是使用以下查询格式获取 varchar(MAX) 字段长度超过 8000 个字符可以从 SQL Server 查询到 R

    query_string <- "SELECT CAST(text_var AS text) AS text_var FROM table_name"
    

    【讨论】:

      猜你喜欢
      • 2011-06-05
      • 2012-07-18
      • 2013-02-15
      • 2012-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-30
      相关资源
      最近更新 更多