RODBC:为啥 sqlQuery() 中的空值和仅空格值的值为“NA”?

Posted

技术标签:

【中文标题】RODBC:为啥 sqlQuery() 中的空值和仅空格值的值为“NA”?【英文标题】:RODBC: Why are the values "NA" for empty and space-only values in sqlQuery()?RODBC:为什么 sqlQuery() 中的空值和仅空格值的值为“NA”? 【发布时间】:2016-12-12 21:16:32 【问题描述】:

我是 R 的新手。我正在查看从以下返回的 RODBC 结果:

> library(RODBC)
> dbcon <- odbcDriverConnect("DRIVER=SQL SERVER;SERVER=MYSERV;DATABASE=SOME", tabQuote='', colQuote='')
> sqlQuery(dbcon, "SELECT 3, 'a', ' ', '', NULL")
    .1 .2 .3 .4
1 3  a NA NA NA

为什么' ''' 返回NA?我在文档中找不到有关此行为的任何内容。我错过了什么?

【问题讨论】:

你可以试试as.is选项 @Hack-R:这会起作用,但我无法弄清楚为什么它首先会尝试转换为 NA,而无需我通过类似 na.strings 的方式告诉它。是否有一些全局参数告诉它?它是怎么知道的? 我不确定。有一个nullstring 参数,但我想空格在技术上不是空的。我想这只是默认行为,因为包作者认为更多时候这将是所需的解释。 【参考方案1】:

所以决定查看 RODBC 的 sqlQuery 方法的源代码,因为我记得它是开源的:

sqlQuery <-
    function(channel, query, errors = TRUE, ..., rows_at_time)

    if(!odbcValidChannel(channel))
       stop("first argument is not an open RODBC channel")
    if(missing(query))
        stop("missing argument 'query'")
    ## could argue that 'max' should restrict rows_at_time
    rows_at_time <- if(missing(rows_at_time)) attr(channel, "rows_at_time")
    else max(1, min(1024, rows_at_time))
    stat <- odbcQuery(channel, query, rows_at_time)
    if(stat == -1L) 
        if(errors) return(odbcGetErrMsg(channel))
        else return(invisible(stat))
     else return(sqlGetResults(channel, errors = errors, ...))

所以它调用sqlGetResults:

sqlGetResults <-
    function (channel, as.is = FALSE,
              errors = FALSE, max = 0, buffsize = 1000,
              nullstring = NA_character_, na.strings = "NA",
              believeNRows = TRUE, dec = getOption("dec"),
              stringsAsFactors = default.stringsAsFactors())

    if(!odbcValidChannel(channel))
       stop("first argument is not an open RODBC channel")
    as.df <- function(value, colnames) 
        for(i in seq_along(value))
            if(is.list(value[[i]])) class(value[[i]]) <- "ODBC_binary"
        ## convert list to data frame
        class(value) <- "data.frame"
        names(value) <- make.unique(colnames)
        row.names(value) <- seq(along=value[[1L]])
        value
    
    cols <- .Call(C_RODBCNumCols, attr(channel, "handle_ptr"))
    ## FIXME: should this be <= 0L?
    if(cols < 0L) 
        if(errors) return("No data")
        else return(invisible(-1L))
    
    cData <- .Call(C_RODBCColData, attr(channel, "handle_ptr"))
    dbdata <- odbcFetchRows(channel,
                            max = max,
                            buffsize = buffsize,
                            nullstring = nullstring,
                            believeNRows = believeNRows)
    if(dbdata$stat < 0L) 
    if(errors) return(odbcGetErrMsg(channel))
    else return(invisible(dbdata$stat))
    
    data <- as.df(dbdata$data, cData$names)
    if(nrow(data) > 0L) 
        cols <- ncol(data)
        enc <- attr(channel, "encoding")
        if(length(na.strings))
            for (i in 1L:cols)
                if(is.character(data[,i]))
                    data[data[,i] %in% na.strings, i] <- NA
        if(is.logical(as.is)) 
            as.is <- rep(as.is, length = cols)
         else if(is.numeric(as.is)) 
            if(any(as.is < 1 | as.is > cols))
                stop("invalid numeric 'as.is' expression")
            i <- rep(FALSE, cols)
            i[as.is] <- TRUE
            as.is <- i
         else if(length(as.is) != cols)
            stop("'as.is' has the wrong length ", length(as.is),
                 " != cols = ", cols)
        for (i in seq_len(cols)) 
            if(is.character(data[[i]]) && nchar(enc))
                data[[i]] <- iconv(data[[i]], from = enc)
            if(as.is[i] || is.list(data[[i]])) next
            if(is.numeric(data[[i]])) next
            if(cData$type[i] == "date")
                data[[i]] <- as.Date(data[[i]])
            else if(cData$type[i] == "timestamp")
                data[[i]] <- as.POSIXct(data[[i]])
            else
                data[[i]] <- type.convert(as.character(data[[i]]),
                                          na.strings = na.strings,
                                          as.is = !stringsAsFactors,
                                          dec = dec)
        
    
    data

所以对于字符串/varchar 值,这一切都归结为:

data[[i]] <- type.convert(as.character(data[[i]]),
                                         na.strings = na.strings,
                                          as.is = !stringsAsFactors,
                                          dec = dec)

那么让我们试试type.convert:

> type.convert("a")
[1] a

好的,这是意料之中的。现在让我们尝试奇怪的情况:

> type.convert("")
[1] NA

hmmm...好的,这就是我们得到NA的方式

> type.convert("       ")
[1] NA

好的,那也是NA。 现在,为什么as.is 不返回NA

if(as.is[i] || is.list(data[[i]])) next

啊哈,它只是在设置as.is 时退出并且不调用type.convert() 这就解释了为什么在设置as.is 标志时它不返回NA

【讨论】:

以上是关于RODBC:为啥 sqlQuery() 中的空值和仅空格值的值为“NA”?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用文本框搜索功能返回同一列中的空值和值

EXCEL函数去除数组中的0值和空值

RODBC-一个sqlQuery()调用中的多个表[重复]

如何让 sum 函数在 RODBC 中的 SQLquery 中工作

为啥派生列组件的表达式不适用于 SSIS 中的空值?

EXCEL这列中的空单元格为啥选“空值”定位不了?