【问题标题】:R DBI SQL Server: dbWriteTable truncates rows / field.types parameter does not workR DBI SQL Server:dbWriteTable 截断行/field.types 参数不起作用
【发布时间】: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


【解决方案1】:

尝试构建尽可能完整的答案 - 让我们先从 RJDBC 项目开始


RJDBC

回购:https://github.com/s-u/RJDBC

在我看来,西蒙可能在这里无意中用他的驱动程序级别类型映射覆盖了所有显式字段类型声明?

maptypes.R

我说这只是一个非常高级的猜测,因为当涉及到RJavaDBI 的交叉点时,我绝对无法理解,但鞋子是否合适?

当我运行类似的东西时:

> dbDataType(jdbc.sql.driver,titanic)
   Class   Sex      Age      Survived  Freq 
  "TEXT"   "TEXT"   "TEXT"   "TEXT"    "DOUBLE" 

似乎驱动程序将这些类型映射到varchar(255),这就是为什么您会看到所有创建的列默认设置为该类型。

无论如何。只是猜测原因,然后继续解决问题。


列和类型

到目前为止,利用上述所有 cmets 和建议,对我来说,在 sql server 中实现这些数据类型的最佳和唯一方法是:

pool <- dbPool(
  drv      = jdbc.sql.driver,
  url      = sql.url,
  dname    = sql.dname,
  schema   = sql.schema,
  username = sql.username,
  password = sql.password
)

conn <- poolCheckout(pool)

dbCreateTable(conn= conn,
              Id(database = "my_project", 
                 schema = "dbo", 
                 table = "titanic"),
                 fields = c( Class="nvarchar(max)",
                             Sex="varchar(max)",
                             Age="ntext",
                             Survived="text",
                             Freq="integer"
                            ),
                 row.names = NULL)

产生:


插入数据

然后从那里的想法是插入类似的行:

dbAppendTable(conn= conn,
              name= Id(database = "my_project", schema = "dbo", table = "titanic"), 
              value = titanic)

但我收到以下错误:

Error in .verify.JDBC.result(r, "Unable to retrieve JDBC result set",  : 
  Unable to retrieve JDBC result set
  JDBC ERROR: The value is not set for the parameter number 2.
  Statement: INSERT INTO "my_project"."dbo"."titanic"
  ("Class", "Sex", "Age", "Survived", "Freq")
VALUES
  (?, ?, ?, ?, ?)
In addition: Warning message:
In if (is.na(v)) { :
  the condition has length > 1 and only the first element will be used

所以现在更好的选择是使用:

values <- DBI::sqlAppendTable(con = conn, table = Id(database = "my_project", schema = "dbo", table = "titanic"), values = titanic)
DBI::dbExecute(conn = conn, values)

产生具有请求列类型的预期行:

【讨论】:

  • 谢谢@sgoley!我使用了您的建议,并且能够使用工作代码。请参阅上面的更新。
  • 太棒了!在您意识到赏金之前,您是否还在寻找其他东西?
  • 谢谢老兄,刚刚把赏金给你了。
猜你喜欢
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-12
  • 1970-01-01
相关资源
最近更新 更多