当保存到 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 是我的第一个链接)。艾伦……不过,我不知道你会在这里得到更好的建议,因为我不知道自上一个问题以来发生了多大的变化。我成功地使用了DBI
和odbc
(包括相当大的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; 使用他们的批量工具(bcp
或sqlcmd
)对“大数据”进行所有查询,我相信他们都能更好地处理它,尽管这比在 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)