【问题标题】:SQL Server BULK INSERT - Inserting DateTime valuesSQL Server BULK INSERT - 插入日期时间值
【发布时间】:2011-10-31 10:34:08
【问题描述】:

我有 600 万行数据要插入到我的 SQL Server 数据库中。我可以使用 600 万条 INSERT 语句以缓慢的方式执行此操作(根据我的计算,它需要 18 小时才能运行),或者我可以尝试 BULK INSERT。

BULK INSERT 存在无法转义字符的问题,但这种情况下的数据非常简单,因此不应遇到此问题。

但是 SQL Server 似乎不喜欢将任何形式的日期/时间数据插入到字段中。

这是表(伪 SQL)

CREATE TABLE Tasks (
    TaskId bigint NOT NULL IDENTITY(1,1) PRIMARY KEY,
    TriggerId bigint NOT NULL FOREIGN KEY,
    Created datetime NOT NULL,
    Modified datetime NOT NULL,
    ScheduledFor datetime NULL,
    LastRan datetime NULL,
    -- and about 10 more fields after this
)

这是我的 BULK INSERT 语句:

SET DATEFORMAT dmy
BULK INSERT Tasks
FROM 'C:\TasksBulk.dat'
WITH (
    -- CHECK_CONSTRAINTS is not necessary as the only constraints are always enforced regardless of this option (UNIQUE, PRIMARY KEY, and NOT NULL)
    CODEPAGE = 'RAW',
    DATAFILETYPE = 'native',

    KEEPIDENTITY,
    MAXERRORS = 1,
    ORDER ( CallId ASC ),

    FIELDTERMINATOR = '\t',
    ROWTERMINATOR   = '\0'
)

这是TasksBulk.dat中的第一行数据:

1000\t1092\t01/01/2010 04:00:17\t01/01/2010 04:00:17\t\t01/01/2010 04:00:14\0

(为了可读性,用 4 个空格替换制表符重新格式化:)

1000    1092    01/01/2010 04:00:17    01/01/2010 04:00:17        01/01/2010 04:00:14\0

但是当我运行 BULK INSERT 语句时,我得到了这个错误:

Msg 4864,级别 16,状态 1,行 2 批量加载数据转换错误 (指定代码页的类型不匹配或无效字符) 第 1 行,第 3 列(已创建)。

我尝试过使用不同的行和字段终止符以及每种不同的日期/时间格式(包括“01/01/2010”、“2010-01-01”,无论有无“04:00:17”时间零件)。我不知道我在这里做错了什么。

【问题讨论】:

    标签: sql sql-server tsql bulkinsert


    【解决方案1】:

    您将CODDEPAGE 设置为RAW(大概是为了速度)。

    错误消息暗示您的数据包含代码页之外的字符。

    CODEPAGE [ = 'ACP' | 'OEM' | 'RAW' | 'code_page' ]
    

    指定数据文件中数据的代码页。 CODEPAGE 是 仅当数据包含 char、varchar 或 text 列时才相关 大于 127 或小于 32 的字符值。

    但这可能会产生误导。您的示例数据行包含缺少的列。如果不使用格式文件,则表中的每个字段都必须使用。

    因此,您可以创建格式文件或使用 varchar(25) 为 datetime 列创建临时表,将临时表导入然后执行更新到目标表。这样您就可以更好地控制转化和丢失数据。

    【讨论】:

    • 无论 CODEPAGE 和 DATAFILETYPE 选项如何,我都会收到相同的错误。两个日期之间的“缺失列”表示 ScheduledFor 列中的 NULL 值。
    • 我知道它代表一个 NULL 值!我是说 BULK INSERT 期望所有值都存在,除非您使用格式文件。
    • 我暂时无法尝试 formatfile 选项,但我确实尝试了您创建临时表的建议(仅使用简单的 varchar/int 数据类型)。我没有收到转换错误,但现在我不断收到“批量加载:数据文件中遇到意外的文件结尾”。不管我把行终止符放在哪里。啊。 (MySQL 在这方面要简单得多,微软提供了什么?)
    • @David: "unexpected end of file" 可能是因为 BULKINSERT 期望每一行的所有列都存在。
    【解决方案2】:

    事实证明,将 DATAFILETYPE 从 'native' 更改为 'char' 解决了这个问题。 'native' 类型意味着所有内容都采用严格的数据格式,而 'char' 意味着更多的纯文本文件。

    【讨论】:

    • Stack Overflow 说我只能在 48 小时后接受自己的答案。谢谢你之前的帮助,顺便说一句。
    【解决方案3】:

    我熟悉的一种方法是以整数形式插入日期。

    我使用从某个日期开始的秒数(我使用过去 10 多年前的秒数,因为我不会访问或生成任何比该日期更早的数据)

    使用 2000 年 1 月 1 日的参考点,日期 2012-01-02 12:15:10.000 将存储为 378637886。

    查询数据库时,您可以使用DateAdd(SS, column_name, '2000-01-01') 返回列。

    如果需要这种精度,您也可以在几毫秒内完成。

    我使用我自己的自定义函数将我的时间(以秒为单位)转换为我想要的任何格式,我使用另一个自定义函数将日期转换回秒。

    我意识到这可能不是一个好方法,因为它可能需要您更改数据库和代码,但也许它可能是其他人会发现有用的解决方案概念。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-16
      • 1970-01-01
      • 2014-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多