将 Unicode 从 R 写入 SQL Server

Jac*_*lis 5 sql-server unicode r

我正在尝试将 Unicode 字符串从 R 写入 SQL,然后使用该 SQL 表来支持 Power BI 仪表板。不幸的是,Unicode 字符似乎只在我将表加载回 R 时才起作用,而在我在 SSMS 或 Power BI 中查看表时则不起作用。

require(odbc)
require(DBI)
require(dplyr)
con <- DBI::dbConnect(odbc::odbc(),
                      .connection_string = "DRIVER={ODBC Driver 13 for SQL Server};SERVER=R9-0KY02L01\\SQLEXPRESS;Database=Test;trusted_connection=yes;")
testData <- data_frame(Characters = "?")
dbWriteTable(con,"TestUnicode",testData,overwrite=TRUE)
result <- dbReadTable(con, "TestUnicode")
result$Characters
Run Code Online (Sandbox Code Playgroud)

成功产生:

> result$Characters
[1] "?"
Run Code Online (Sandbox Code Playgroud)

但是,当我在 SSMS 中拉出该表时:

SELECT * FROM TestUnicode
Run Code Online (Sandbox Code Playgroud)

我得到两个不同的字符:

Characters
~~~~~~~~~~
â¤
Run Code Online (Sandbox Code Playgroud)

这些字符也出现在 Power BI 中。如何正确地将心形字符拉到 R 之外?

Jac*_*lis 13

事实证明,这是 R/DBI/ODBC 驱动程序中某个地方的错误。问题是 R 将字符串存储为 UTF-8 编码,而 SQL Server 将它们存储为 UTF-16LE 编码。此外,当 dbWriteTable 创建表时,默认情况下它会为甚至不能包含 Unicode 字符的字符串创建一个 VARCHAR 列。因此,您需要:

  1. 将 R 数据框中的列从字符串列更改为 UTF-16LE 原始字节的列表列。
  2. 使用 dbWriteTable 时,将字段类型指定为 NVARCHAR(MAX)

这似乎仍然应该由 DBI 或 ODBC 或其他东西来处理。

require(odbc)
require(DBI)

# This function takes a string vector and turns it into a list of raw UTF-16LE bytes. 
# These will be needed to load into SQL Server
convertToUTF16 <- function(s){
  lapply(s, function(x) unlist(iconv(x,from="UTF-8",to="UTF-16LE",toRaw=TRUE)))
}

# create a connection to a sql table
connectionString <- "[YOUR CONNECTION STRING]"
con <- DBI::dbConnect(odbc::odbc(),
                      .connection_string = connectionString)

# our example data
testData <- data.frame(ID = c(1,2,3), Char = c("I", "?","Apples"), stringsAsFactors=FALSE)

# we adjust the column with the UTF-8 strings to instead be a list column of UTF-16LE bytes
testData$Char <- convertToUTF16(testData$Char)

# write the table to the database, specifying the field type
dbWriteTable(con, 
             "UnicodeExample", 
             testData, 
             append=TRUE, 
             field.types = c(Char = "NVARCHAR(MAX)"))

dbDisconnect(con)
Run Code Online (Sandbox Code Playgroud)