【问题标题】:MySQL LOAD DATA INFILE: works, but unpredictable line terminatorMySQL LOAD DATA INFILE:有效,但不可预测的行终止符
【发布时间】:2012-06-11 17:00:25
【问题描述】:

MySQL 有一个不错的 CSV 导入功能LOAD DATA INFILE

我有一个大型数据集,需要定期从 CSV 导入,所以这个功能正是我所需要的。我有一个可以完美导入数据的工作脚本。

.....except....我事先不知道行尾终止符会是什么。

我的 SQL 代码目前看起来像这样:

LOAD DATA INFILE '{fileName}'
 INTO TABLE {importTable}
 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
 LINES TERMINATED BY '\n'
 IGNORE 1 LINES
( {fieldList} );

这对某些导入文件非常有用。

但是,导入数据来自多个来源。其中一些具有\n 终结符;其他人有\r\n。我无法预测我会拥有哪一个。

有没有办法使用LOAD DATA INFILE 来指定我的行可以用\n\r\n 终止?我该如何处理?

【问题讨论】:

  • 您是否需要处理\n(大多数*nix 系统,包括OS X)和\r\n (Windows)——在这种情况下@Devart's answer看起来很完美——或者您是否会遇到其他行终止序列,例如 \n\r(例如来自 RISC OS)、\r(例如 Mac OS 9)和其他?
  • @eggyal - 这是一个非常好的问题。我只考虑了 *nix/Windows 场景,但正如我所说,导入文件的创建超出了我的控制范围,所以我想我需要满足任何需求。我采用了一个基本的预处理解决方案:Devart 的解决方案看起来很棒,但有太多弱点使其足够健壮。
  • 遗憾的是,在这种情况下,我认为没有任何简单的解决方案。可以解析每个候选行终止序列的文件计数出现以试图猜测它使用哪个,但要注意包含多行文本字段的文件,其中行终止与实际记录终止不同(您可能必须计算/验证每个记录终止符之间出现的预期字段数)。我不知道您的应用程序使用什么代码/框架,但很可能有一些库可以在这里为您提供帮助。否则,提示用户告诉你它是什么。

标签: mysql csv delimiter end-of-line


【解决方案1】:

我只是对其进行预处理。作为导入过程的一部分,从命令行工具将 \r\n 更改为 \n 的全局搜索/替换应该简单且高效。

【讨论】:

  • 目前看来这是我最好的选择。我希望有一个纯 SQL 解决方案,但如果它不存在,则必须执行脚本预处理步骤。将等待看看是否有更好的答案出现,但如果没有,那么这可能就是那个。
  • 是的。大多数从各种来源提取数据的 ETL 系统最终都会进行一些预处理。如果您所要做的只是规范行终止符,您可能应该算上您的祝福;)
  • 虽然@Devart 的回答表面上看起来不错,但我使用了一个简单的预处理实用程序。看起来它会更健壮,而且正如你所说,它的性能相当不错。很想看到一个纯 SQL 解决方案,但看起来没有。 (似乎很奇怪,无法为 LOAD DATA INFILE 提供接受任何行终止符的选项,但事实并非如此,所以到此结束)
【解决方案2】:

您还可以查看其中一个数据集成包。 Talend Open Studio 具有非常灵活的数据输入例程。例如,您可以使用一组分隔符处理文件并捕获拒绝并以另一种方式处理它们。

【讨论】:

    【解决方案3】:

    我假设您只需要通过 mysql 而不是任何编程语言来获取信息。 如果您有记事本++,则在使用加载数据之前将格式转换为 windows 格式 \r\n (CR LF)。然后处理Load数据查询。确保 LINES TERMINATED BY '\r\n'

    编辑:

    因为编辑器通常不适合转换较大的文件。对于较大的文件,windows 和 linux 都经常使用以下命令

    1) 在windows中转换成windows格式

    TYPE [unix_file] | FIND "" /V > dos_file
    

    2) 在linux中转换成windows格式

    unix2dos  [file]
    

    其他命令也可用

    Windows 格式文件可以通过简单地删除所有 ASCII CR \r 字符转换为 Unix 格式 tr -d '\r' 输出文件

    grep -PL $'\r\n' myfile.txt # show UNIX format  style file (LF terminated)
    grep -Pl $'\r\n' myfile.txt # show WINDOS format style file (CRLF terminated)
    

    在 linux/unix 中,file 命令检测所使用的行尾 (EOL) 类型。因此可以使用此命令检查文件类型

    【讨论】:

    • 这不是我希望的答案,但这种转换看起来是我目前最好的选择。但不是在像 Notepad++ 这样的程序中,因为文件是几百兆;最好使用命令行工具。
    • @SDC 查看编辑部分以了解 windows 和 unix 的命令行转换
    【解决方案4】:

    您可以将行分隔符指定为“\n”,并在加载过程中从最后一个字段中删除尾随的“\r”分隔符。

    例如-

    假设我们有“entries.txt”文件。行分隔符是'\r\n',只有ITEM2 | CLASS3 | DATE2行之后的分隔符是'\n':

    COL1  | COL2   | COL3
    ITEM1 | CLASS1 | DATE1
    ITEM2 | CLASS3 | DATE2
    ITEM3 | CLASS1 | DATE3
    ITEM4 | CLASS2 | DATE4
    

    CREATE TABLE 语句:

    CREATE TABLE entries(
      column1 VARCHAR(255) DEFAULT NULL,
      column2 VARCHAR(255) DEFAULT NULL,
      column3 VARCHAR(255) DEFAULT NULL
    )
    

    我们的 LOAD DATA INFILE 查询:

    LOAD DATA INFILE 'entries.txt' INTO TABLE entries
    FIELDS TERMINATED BY '|'
    LINES TERMINATED BY '\n'
    IGNORE 1 LINES
    (column1, column2, @var)
    SET column3 = TRIM(TRAILING '\r' FROM @var);
    

    显示结果:

    SELECT * FROM entries;
    +---------+----------+---------+
    | column1 | column2  | column3 |
    +---------+----------+---------+
    | ITEM1   |  CLASS1  |  DATE1  |
    | ITEM2   |  CLASS3  |  DATE2  |
    | ITEM3   |  CLASS1  |  DATE3  |
    | ITEM4   |  CLASS2  |  DATE4  |
    +---------+----------+---------+
    

    【讨论】:

    • 啊,现在 this 看起来很有希望.... :-) 我会进一步调查,让你知道它是怎么回事。谢谢你的建议。
    • 如果引用了 CSV 中的 COL3 怎么办?当 MySQL 在引用字段的结尾和行终止之间遇到意外字符时,这不会导致解析器错误吗?如果column3 是未引用的文本,它应该\r 字符结尾(可能会更好地做一些更丑陋的事情,包括测试最后一个字符,如果是\r,那么只删除那个字符)?
    • 我还考虑了引用的 COL3。我已经尝试了一些变体,并且可以说引用的 COL3 会导致行终止问题。所以,最后一个字段应该不加引号。关于...\r\r\r - 是的,可以重写此方法以仅删除最后一个\r
    • 在没有接受的情况下,我不知道这是否有效,而且我知道 cmets 提出的观点,但我给你赏金,因为这是唯一的答案试图在 sql 查询范围内解决问题。预处理文件始终是最简单的方法,但很高兴看到仅使用 sql 的创造性解决方案。谢谢。
    • 我采用了一个简单的预处理解决方案。我真的很喜欢你的想法,但与上面的 cmets 一样,我不能依赖它来处理所有输入。导入文件来自多个第三方,所以我对内容没有足够的控制权来知道我不会被引号等绊倒。(我想如果我确实有那种控制,这个问题会无论如何都没有被问到)。但是感谢您的建议;这很有趣,很有创意。
    【解决方案5】:

    如果第一次加载有 0 行,则对另一个行终止符执行相同的语句。这应该可以通过一些基本的计数逻辑来实现。

    至少它保留在 SQL 中,如果它在你第一次获胜时工作。并且可以减少重新扫描所有行并删除特定字符的麻烦。

    【讨论】:

      【解决方案6】:

      为什么不先看看这些行是如何结束的?

      $handle = fopen('inputFile.csv', 'r');
      
      $i = 0;
      if ($handle) {
          while (($buffer = fgets($handle)) !== false) {
      
              $s =  substr($buffer,-50);
      
              echo $s; 
              echo preg_match('/\r/', $s) ? 'cr ' : '-- ';
              echo preg_match('/\n/', $s) ? 'nl<br>' : '--<br>';          
      
              if( $i++ > 5)
                  break;
      
          }
      
          fclose($handle);
      }
      

      【讨论】:

      • 这帮助我解决了这个问题——一旦你确定了行尾,你就可以将 SQL 命令包装成一个条件语句。
      【解决方案7】:

      您可以使用 LINES STARTING 将文本中的常用行结尾和新行分开:

      LOAD DATA LOCAL INFILE '/home/laptop/Downloads/field3-utf8.csv' 
      IGNORE INTO TABLE Field FIELDS 
      TERMINATED BY ';' 
      OPTIONALLY ENCLOSED BY '^' 
      LINES STARTING BY '^' 
      TERMINATED BY '\r\n' 
      (Id, Form_id, Name, Value)
      

      对于带有 " 封闭字符的普通 CSV 文件,它将是:

      ...
      LINES STARTING BY '"' 
      ...
      

      【讨论】:

        猜你喜欢
        • 2018-03-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-05
        • 2010-11-17
        • 1970-01-01
        相关资源
        最近更新 更多