当保存到 SQL Server 到 varchar(255) 时,RODBC::sqlsave() 会截断 DF 中的 col

Posted

技术标签:

【中文标题】当保存到 SQL Server 到 varchar(255) 时,RODBC::sqlsave() 会截断 DF 中的 col【英文标题】:RODBC::sqlsave() truncates col in DF when saving to SQL Server to varchar(255) 【发布时间】:2020-06-24 05:02:19 【问题描述】:

RODBC:sqlsave 默认写入一个只有 char 列的 data.frame 作为varchar(255) 并截断其余部分。 DF 中的 var 实际上长度接近 4000 个字符。

我试过了:

sqlQuery(db,'CREATE TABLE SCUBA_tweetsC8 (user_id``` varchar(max), 
status_id varchar(max), 
screen_name varchar(max), 
text varchar(max),
source varchar(max),
reply_to_status_id varchar(max), 
reply_to_user_id varchar(max), 
reply_to_screen_name varchar(max), 
ext_media_type varchar(max), 
lang varchar(max),
quoted_status_id varchar(max), 
quoted_text varchar(max), 
quoted_source varchar(max), 
quoted_user_id varchar(max), 
quoted_screen_name varchar(max), 
quoted_name varchar(max), 
quoted_location varchar(max), 
quoted_description varchar(max), 
retweet_status_id varchar(max),
retweet_text varchar(max), 
retweet_source varchar(max), 
retweet_user_id varchar(max),
retweet_screen_name varchar(max), 
retweet_name varchar(max), 
retweet_location varchar(max), 
retweet_description varchar(max), 
place_url varchar(max), 
place_name varchar(max),
place_full_name varchar(max), 
place_type varchar(max), 
country varchar(max), 
country_code varchar(max), 
status_url varchar(max), 
name varchar(max),
location varchar(max), 
description varchar(max), 
url varchar(max), 
profile_url varchar(max), 
profile_expanded_url varchar(max), 
profile_banner_url varchar(max), 
profile_background_url varchar(max), 
profile_image_url varchar(max),);')

sqlSave(db,SCUBA_tweetsC,"SCUBA_tweetsC8",append = T)

但我收到以下错误:

odbcUpdate 中的错误(通道、查询、mydata、coldata[m、]、test = test、:'Calloc' 无法分配内存(18446744071562067968,共 1 个字节)

我已尽我所能尝试其他建议,例如尝试使用 VarType 函数但没有成功。

【问题讨论】:

您的问题与此问题重复:***.com/questions/39129895/…。搜索“odbcUpdate 中的错误(通道、查询、mydata、coldata[m, ]、test = test, : 'Calloc' 无法分配内存(18446744071562067968 of 1 bytes)”时的最佳 Google 结果 Alex,它实际上只能是一个带有答案的副本,接受或其他方式(尽管“接受”是更可取的),而 cmets 最好的做法是建议使用另一个包。 @r2evans - 谷歌搜索此错误返回 GitLab 问题(第二个链接),尚未解决......我怀疑我们可以在这里提供帮助。 Axex,我同意(尽管 github 是我的第一个链接)。艾伦……不过,我不知道你会在这里得到更好的建议,因为我不知道自上一个问题以来发生了多大的变化。我成功地使用了DBIodbc(包括相当大的nvarchar(max) 字段),也许这是您调查的途径。否则,正如 joran 在one of the comments by Alex's linked question 中提到的,也许是 r-sig-db 邮件,不过我没有这方面的经验。 感谢您的信息。我想我会问一个希望有人有一个可行的选择。 @r2evans,你能发布一个例子吗?我一直在使用 DBI 和 ODBC,并且还收到了截断错误,并且不会将任何内容写入 SQL 表。 【参考方案1】:

我不使用RODBC,所以我无法测试/重现您的问题,但我会尝试重现您的情况并证明在我的环境中它不会失败。

样本数据:

library(tibble)
dat <- tibble(id = 1:2, chr = c(strrep("A", 4000), strrep("B", 400000)))
nchar(dat$chr)
# [1]   4000 400000

library(DBI)
# library(odbc) # no need to load, but need it installed/available
con <- DBI::dbConnect(odbc::odbc(), driver = "ODBC Driver 17 for SQL Server",
                      database = "mydb", server = "111.222.333.444,1433",
                      uid = "myuser", pwd = "mypassword")

(我不打算介绍所有需要的选项。)

手动定义表格

DBI::dbExecute(con, "drop table if exists r2test")
# [1] 0
DBI::dbExecute(con, "create table r2test (id int, chr nvarchar(max))")
# [1] 0
system.time(
  DBI::dbWriteTable(con, "r2test", dat, append = TRUE)
)
#    user  system elapsed 
#    0.00    0.02    1.28 
dat2 <- DBI::dbGetQuery(con, "select id, chr from r2test")
nchar(dat2$chr)
# [1]   4000 400000
str(dat2)
# 'data.frame': 2 obs. of  2 variables:
#  $ id : int  1 2
#  $ chr: chr  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"| __truncated__ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"| __truncated__

证明pre-CreateTable

我发现 SQL Server 和 DBI 存在两种可能的错误情况。

DBI::dbExecute(con, "drop table if exists r2test")
### also with DBI::dbCreateTable(con2, "r2test", dat)
DBI::dbWriteTable(con, "r2test", dat, create = TRUE)
# Error: nanodbc/nanodbc.cpp:1617: 42000: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Incorrect syntax near '4e+05'.  [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Statement(s) could not be prepared. 
# <SQL> 'CREATE TABLE "r2test" (
#   "id" INT,
#   "chr" varchar(4e+05)
# )
# '

这是因为 SQL Server 似乎不喜欢字段大小的科学记数法。我们可以通过更改scipen来避免这种情况:

options(scipen=99)
DBI::dbWriteTable(con, "r2test", dat, create = TRUE)
# Error: nanodbc/nanodbc.cpp:1617: 42000: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]The size (400000) given to the column 'chr' exceeds the maximum allowed for any data type (8000).  [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Statement(s) could not be prepared. 
# <SQL> 'CREATE TABLE "r2test" (
#   "id" INT,
#   "chr" varchar(400000)
# )
# '

现在我们看到 SQL Server 不喜欢这么大的显式大小,所以我们需要鼓励它使用varchar(max)

预建表

DBI::dbExecute(con, "drop table if exists r2test")
DBI::dbCreateTable(con2, "r2test", fields = c(id="INT", chr="nvarchar(max)"))
system.time(
  DBI::dbWriteTable(con, "r2test", dat, append = TRUE)
)
#    user  system elapsed 
#    0.00    0.01    1.34 
dat3 <- DBI::dbGetQuery(con, "select id, chr from r2test")
nchar(dat3$chr)
# [1]   4000 400000
str(dat3)
# 'data.frame': 2 obs. of  2 variables:
#  $ id : int  1 2
#  $ chr: chr  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"| __truncated__ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"| __truncated__

关于“大字段”的注意事项

在为 SQL Server 使用 Microsoft ODBC 驱动程序时,必须始终在查询中选择“大”字段last。例如,

DBI::dbGetQuery(con, "select chr, id from r2test")
# Error in result_fetch(res@ptr, n) : 
#   nanodbc/nanodbc.cpp:2966: 07009: [Microsoft][ODBC Driver 17 for SQL Server]Invalid Descriptor Index 

这是一个已知的事情,使用 MS 的 SQL Server odbc 驱动程序(驱动程序“ODBC Driver 17 for SQL Server”)。这个问题已经存在了。没有其他驱动程序(包括连接到 SQL Server 的 FreeTDS)倾向于这种“功能”。没有迹象表明这会改变(事实上,它在文档中是“正式的”,尽管“大”没有被量化)。

我不知道RODBC 是否也有这个问题;由于它不使用nanodbc,它可能更智能地使用SQLGetData 函数,从而避开问题。

解决此问题的方法:

始终将“大数据”放在所选列列表的末尾; 使用FreeTDS 而不是微软的 ODBC 驱动程序...据说它稍微慢一些(10%?idk),但我已经成功安装在 windows/linux 中,并且以疯狂的顺序选择字段没有问题; 如果您有合适的操作系统和 RStudio 的专业产品之一,请使用 RStudio's professional drivers; 使用他们的批量工具(bcpsqlcmd)对“大数据”进行所有查询,我相信他们都能更好地处理它,尽管这比在 R 控制台上的交互性要少得多; 使用RODBC(据称...再次,我不知道); 不要使用“大数据”字段(...(max) 或大于...(255) 的任何内容...一个未明确定义的数字)...也许不是一个选项; -- 最近,一个 PR (odbc!415) 已经确定了解决这个大范围问题的能力,所以包的 github-install(直到在 CRAN 上发布)将起作用;或 使用与 SQL Server 不同的 DBMS ...也许不是一种选择。

参考资料:

odbc 包中,问题odbc/#10 似乎是第一次出现,并在此包中的所有其他包中被引用;相关的已解决问题 (#82, #86, #112, #171, #256, #331);最近的一些讨论可能会解决这个问题(#309, #358, #373 )

最终,odbc 使用了nanodbc C++ 库,虽然他们认识到了问题,但他们觉得这不是他们要解决的问题 (nanodbc/#149)。

微软源文档:https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/getting-long-data

【讨论】:

以上是关于当保存到 SQL Server 到 varchar(255) 时,RODBC::sqlsave() 会截断 DF 中的 col的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2008 中的 Varchar 到日期时间

SQL Server varchar列试图在SSRS中显示换行符

什么 MS SQL Server 类型映射到 Types.VARCHAR

SQL Server中varchar(n)和nvarchar(n)

无法使用 SSIS 将 SQL Server varchar(max) 传输到 MySQL 文本

在SQL Server触发器中转换(varchar到int)