【发布时间】:2021-06-26 02:22:54
【问题描述】:
我正在使用带有sqljdbc42.jar 驱动程序的 SQL Server 数据库和 JDBC 和池。
代码:
library(DBI)
library(RJDBC)
library(pool)
jar.path.ms.sql.driver <- "./www/base/sql_drivers/sqljdbc42.jar"
jdbc.sql.driver <- JDBC(
driverClass = "com.microsoft.sqlserver.jdbc.SQLServerDriver",
classPath = jar.path.ms.sql.driver
)
sql.url <- "jdbc:sqlserver://sql01" # our server url
sql.dname <- "my_projects"
sql.username <- "user"
sql.password <- "password"
pool <- dbPool(
drv = jdbc.sql.driver,
url = sql.url,
dname = sql.dname,
username = sql.username,
password = sql.password
)
到目前为止,一切都很好。我可以使用dbReadTable 从数据库中读取表。我还可以使用dbWriteTable 将表写入数据库。对于写入案例,我在数据框中有要写入很长字符串的列。在某些情况下,字符串的长度超过 255 个字符。
我曾希望使用dbWriteTable 函数的field.types 参数来正确配置SQL Server 列(comments 是我要写入数据库的数据框):
conn <- poolCheckout(pool)
DBI::dbWriteTable(conn = conn,
DBI::Id(cluster = "my_projects", schema = "dbo", table = "comments"),
value = comments,
overwrite = TRUE,
row.names = FALSE,
field.types = c(
STRATEGIC_AREA = "varchar(255)",
OBJECTIVE = "varchar(255)",
METHOD_OF_MEASURE = "varchar(MAX)",
TARGET = "float",
UNIT = "varchar(255)",
MIN_MAX = "varchar(255)",
JUL = "varchar(1024)",
AUG = "varchar(MAX)",
SEP = "varchar(MAX)",
OCT = "varchar(MAX)",
NOV = "varchar(MAX)",
DEC = "varchar(MAX)",
JAN = "varchar(MAX)",
FEB = "varchar(MAX)",
MAR = "varchar(MAX)",
APR = "varchar(MAX)",
MAY = "varchar(MAX)",
JUN = "varchar(MAX)"
))
poolReturn(conn)
poolClose(pool)
问题在于 SQL Server 数据库忽略了列类,并与 varchar(255) 列一致地创建。 field.types 中的 varchar(1024) 或 varchar(MAX) 等列类将被忽略。该函数似乎遵循数据框列类的映射:
- char -> varchar(255)
- num -> 浮动
如果我用"my_projects.dbo.comments" 替换DBI::Id(cluster = "my_projects", schema = "dbo", table = "comments") 似乎没有什么区别。如果我引用传递给field.types 的命名字符向量的左侧也没关系。
因此,包含长度超过 255 个字符的字符串的数据帧行被截断(这些行被跳过),与数据帧相比,SQL Server 表中的行数更少。错误信息(很长的字符串位于FEB 列中):
.local(conn, statement, ...) 中的错误:
在 dbSendUpdate 中执行 JDBC 更新查询失败(表 'my_projects.dbo.cmets' 列 'FEB' 中的字符串或二进制数据将被截断。截断值:
我错过了什么?
更新
根据下面sgoley的更新,我将代码更改如下:
dbCreateTable(
conn = conn,
Id(
database = "my_projects",
schema = "dbo",
table = "comments"
),
fields = c(
STRATEGIC_AREA = "varchar(255)",
OBJECTIVE = "varchar(255)",
METHOD_OF_MEASURE = "varchar(255)",
TARGET = "float",
UNIT = "varchar(255)",
MIN_MAX = "varchar(255)",
JUL = "varchar(MAX)",
AUG = "varchar(MAX)",
SEP = "varchar(MAX)",
OCT = "varchar(MAX)",
NOV = "varchar(MAX)",
DEC = "varchar(MAX)",
JAN = "varchar(MAX)",
FEB = "varchar(MAX)",
MAR = "varchar(MAX)",
APR = "varchar(MAX)",
MAY = "varchar(MAX)",
JUN = "varchar(MAX)"
),
row.names = NULL
)
values <- DBI::sqlAppendTable(con = conn,
table = Id(database = "my_projects", schema = "dbo", table = "comments"),
values = comments,
row.names = FALSE)
RJDBC::dbSendUpdate(conn, values)
现在可以正常工作,没有任何问题和错误消息。使用 Microsoft SQL Server Management Studio 进行的验证确认这些列现在是正确的类并遵循 fields 规范。
【问题讨论】:
-
我认为
sqljdbc42相对较旧,因为它适用于 JRE 8 应用程序。您是否尝试过使用varchar(8000),这是旧实现的最大字段长度?也可能varchar(-1)可以欺骗某些实现发送varchar(max),但我不知道 R DBI 会如何反应。 -
我改为
mssql-jdbc-9.2.1.jre8.jar似乎并没有改变这种行为。我将您的建议用于varchar(-1)和varchar(8000)。同样,没有变化。我使用 MS SQL server management studio 18 检查数据库中的结果表。它告诉我character列总是varchar(255)。 -
你有可能测试 SQL SERVER Native Client 吗:从我用它做的几个测试来看,我没有看到 255 个字符的限制。
-
我之前的评论暗示使用
odbc而不是jdbc -
@PaulvanOppen 您使用的是什么版本的 SQL Server?
标签: r sql-server dbi rjdbc