【问题标题】:Omitting columns when importing CSV into Sqlite将 CSV 导入 Sqlite 时省略列
【发布时间】:2015-10-27 14:51:35
【问题描述】:

假设您在 CSV 中有以下数据:

Name, Age, Gender
Jake, 40, M
Bill, 17, M
Suzie, 21, F

导入上述 CSV 时是否可以排除 Age 变量?我目前的方法是简单地使用cut shell 命令。

更新

iluvcapra 为小型 CSV 提供了很好的解决方案。但是,对于非常大的 CSV,这种方法效率低下。例如,假设上面的 CSV 非常大,可以说是 30Gb。加载所有年龄数据只是为了立即删除是浪费时间。考虑到这一点,是否有更有效的方法将列子集加载到 sqlite 数据库中?

我怀疑最好的选择是使用 shell 命令cut 剔除不必要的列。这种直觉正确吗?使用 shell 命令将 CSV 文件预处理为对 sqlite 更友好的版本是否常见?

【问题讨论】:

    标签: sql sqlite shell csv unix


    【解决方案1】:

    如果您希望避免对 SQLite 进行不必要的阅读,并且如果您希望避免在 CSV 文件上使用标准文本处理工具(例如 cutawk)的危险,一种可能性是使用您最喜欢的 csv2tsv 转换器 (*),如下所示:

    csv2tsv input.csv | cut -f 1,3- > tmp.tsv
    
    cat << EOF | sqlite3 demo.db
    drop table if exists demo;
    .mode csv
    .separator "\t"
    .import tmp.tsv demo
    EOF
    
    /bin/rm tmp.tsv
    

    但请注意,如果 input.csv 有文字制表符或换行符或转义双引号,则 以上是否会产生预期的效果将取决于使用的 csv2tsv。

    (*) csv2tsv

    如果您没有合适的 csv2tsv 转换器,这里有一个简单的 python3 脚本可以完成这项工作,处理嵌入的文字换行符、制表符和两个字符序列"\t""\n",在 CSV 中:

    #!/usr/bin/env python3
    
    # Take care of embedded tabs and newlines in the CSV
    
    import csv, re, sys
    
    if len(sys.argv) > 2 or (len(sys.argv) > 1 and sys.argv[1] == '--help'):
        sys.exit("Usage: " + sys.argv[0] + " [input.csv [output.tsv]]")
    
    csv.field_size_limit(sys.maxsize)
    
    if len(sys.argv) == 3:
        out=open(sys.argv[2], 'w+')
    else:
        out=sys.stdout
    
    if len(sys.argv) == 1:
        csvfile=sys.stdin
    else:
        csvfile=open(sys.argv[1])
    
    # tabs and newlines ...
    def edit(s):
      s=re.sub(r'\\t', r'\\\\t', s)
      s=re.sub(r'\\n', r'\\\\n', s)
      s=re.sub('\t', r'\\t', s)
      return re.sub('\n', r'\\n', s)
    
    reader = csv.reader(csvfile, dialect='excel')
    for row in reader:
        line=""
        for s in row:
           s=edit(s)
           if len(line) == 0:
              line = s
           else:
              line += '\t' + s
        print(line)
    
    

    【讨论】:

      【解决方案2】:

      使用 age 列创建一个临时表,然后使用 INSERT... SELECT 将临时表中的数据移动到主表中:

      CREATE TEMP TABLE _csv_import (name text, age integer, gender text);
      .separator ","
      .import file.csv test
      
      INSERT INTO names_genders (name, gender) SELECT name, gender
          FROM _csv_import WHERE 1;
      DROP TABLE _csv_import;
      

      编辑:更新到带有虚拟年龄列的视图:

      CREATE VIEW names_ages_genders AS 
          SELECT (name, 0 AS age ,gender) FROM names_genders;
      CREATE TRIGGER lose_age
          INSTEAD OF INSERT ON names_ages_genders
          BEGIN
              INSERT INTO names_genders (name, gender) 
                  VALUES (NEW.name, NEW.gender)
          END;
      

      这将创建一个名为names_ages_genders 的视图,它会说每个人都是零岁,并且会默默地从任何调用它的INSERT 语句中删除年龄字段。未测试! (实际上我不确定.import 是否可以导入视图。)

      【讨论】:

      • 谢谢!这对我的例子来说很好。但是,对于大型 CSV 文件(几 GB),这是相当低效的。我不喜欢加载所有数据只是在加载后立即删除一些数据的想法。
      • "过早的优化是万恶之源。"如果您使用临时表,这一切都发生在 RAM 中。您还可以在导入表上创建一个VIEW,该表有一个额外的列,该列未映射到INSTEAD OFINSERT 挂钩中的任何列。这样,您可以使用单个 .import 将其导入,插入视图而不是表格。
      • cut 也可以工作,但cut.import 实际上并没有对 CSV 文件做正确的事情,它们没有正确处理转义。我认为有一个 sqlite 模块可以将 CSV 文件映射到虚拟表 your mileage may vary
      • (添加视图/插入触发器来回答)
      • 我想你的意思是说 .import file.csv _csv_import
      猜你喜欢
      • 2023-03-05
      • 2013-02-03
      • 2015-09-23
      • 2012-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-20
      • 1970-01-01
      相关资源
      最近更新 更多