【问题标题】:Export content of a SQLite3 table in CSV以 CSV 格式导出 SQLite3 表的内容
【发布时间】:2013-05-11 15:42:59
【问题描述】:

我有一个生成 SQLite3 数据库的 Ruby 脚本。

我希望能够生成包含其中一个数据库表的“output.csv”文件。

有没有办法在 Ruby 中处理这个问题?

【问题讨论】:

    标签: ruby csv sqlite


    【解决方案1】:

    Sequelto_csv 很容易:

    require 'sequel'
    DB = Sequel.sqlite
    # since Sequel 3.48.0 to_csv is deprecated, 
    # we must load the to_csv feature via a extension
    DB.extension(:sequel_3_dataset_methods) #define to_csv 
    DB.create_table(:test){
      Fixnum :one
      Fixnum :two
      Fixnum :three
    }
    #Prepare some test data
    5.times{|i|
      DB[:test].insert(i,i*2,i*3)
    }
    
    File.open('test.csv', 'w'){|f|
      f << DB[:test].to_csv
    }
    

    结果是:

    one, two, three
    0, 0, 0
    1, 2, 3
    2, 4, 6
    3, 6, 9
    4, 8, 12
    

    在我的测试中,我遇到了行尾问题,所以我需要一个额外的gsub

    File.open('test.csv', 'w'){|f|
      f << DB[:test].to_csv.gsub("\r\n","\n")
    }
    

    如果您想要不带标题行的导出,请使用to_csv(false)

    备注:

    • 自 Sequel 3.48.0 (2013-06-01) 起,.to_csv 已被弃用。 您可以使用带有gem 'sequel', '&lt; 3.48.0' 的旧版本或加载扩展程序sequel_3_dataset_methods)。

    要获得对其他分隔符和其他 CSV 功能的支持,您可以结合使用 Sequel 和 CSV:

    require 'sequel'
    require 'csv'
    #Build test data
    DB = Sequel.sqlite
    DB.create_table(:test){
      Fixnum :one
      Fixnum :two
      Fixnum :three
      String  :four
    }
    #Prepare some test data
    5.times{|i|
      DB[:test].insert(i,i*2,i*3, '<a href="www.test.com">test, no %i</a>' % i)
    }
    
    #Build csv-file
    File.open('test.csv', 'w'){|f|
      DB[:test].each{|data| 
        f << data.values.to_csv(:col_sep=>';')
      }
    }
    

    结果:

    0;0;0;"<a href=""www.test.com"">test, no 0</a>"
    1;2;3;"<a href=""www.test.com"">test, no 1</a>"
    2;4;6;"<a href=""www.test.com"">test, no 2</a>"
    3;6;9;"<a href=""www.test.com"">test, no 3</a>"
    4;8;12;"<a href=""www.test.com"">test, no 4</a>"
    

    作为替代方案,您可以修补 Sequel::Dataset(修改代码来自 marcalc at Github 的帖子):

    class Sequel::Dataset
        require 'csv'
        #
        #Options: 
        #* include_column_titles: true/false. default true
        #* Other options are forwarded to CSV.generate
        def to_csv(options={})
          include_column_titles = options.delete(:include_column_titles){true}  #default: true
          n = naked
          cols = n.columns
          csv_string = CSV.generate(options) do |csv|
            csv << cols if include_column_titles
            n.each{|r| csv << cols.collect{|c| r[c] } }
          end
          csv_string
        end 
    end
    

    【讨论】:

    • 嗯,逗号和行尾可能是个问题,因为我的字段中会有用户生成的 html 代码。 “.mode csv”中内置的sqlite很好地避免了逗号和行尾,有没有办法直接从ruby调用它?
    • 我扩展了我的答案。该示例使用了另一个列分隔符,并且 " 被屏蔽了。
    【解决方案2】:
    # Assume that model is an activerecord model
    @secrets = Model.all
    @csv = CSV.generate do |csv|
        @secrets.each { |secret|
                csv << ["#{secret.attr1.to_s}", "#{secret.attr2.to_s"] # and so on till your row is finished
        }
    end
    render :text => @csv, :content_type => 'application/csv'
    

    如果您还有其他问题,请发表评论。

    【讨论】:

    • 我不确定。如果我从 sqlite3 命令行导出 csv,它将处理字段内的转义逗号、换行符等。您的方法是否足够安全地处理字段内的 html 代码?
    • 应该是。您只需要使用activerecord 管理您的数据库
    • 如果它是非 Rails 代码库,那么 Active Record 与 Sequel 相比几乎没有优势。
    【解决方案3】:

    添加 2020 年的更新。自 Sequel v5 以来,sequel_3_dataset_methods 已被完全删除且不可用。因此,生成 CSV 作为数据库扩展也已被完全删除。

    目前的“最佳实践”似乎是将csv_serializer 插件添加到Sequel::Model 类。不过这里有一个问题,您定义的Sequel::Model必须 在调用Sequel.connect 之后定义。继承 Sequel::Model 的行为调用从数据库中读取。

    这会阻止将您的类预定义为任何通用 Gem 的一部分的典型工作流程。

    根据 Sequel 作者的说法,执行此操作的首选方法是通过 MyClass = Class.new(Sequel::Model(:tablename)) 内联,或者仅在您的方法定义中调用 require

    不承诺效率,这是一个定义“最佳实践”的代码示例

    require 'sequel'
    require 'csv'
    
    module SequelTsv
      class One
        def self.main
          db = Sequel.connect('sqlite://blog.db') # requires sqlite3
    
          db.create_table :items do
            primary_key :id
            String :name
            Float :price
          end
    
          items = db[:items] # Create a dataset
    
          items.insert(:name => 'abc', :price => rand * 100)
          items.insert(:name => 'def', :price => rand * 100)
          items.insert(:name => 'ghi', :price => rand * 100)
    
          item_class = Class.new(Sequel::Model(:items))
    
          item_class.class_eval do
            plugin :csv_serializer
          end
    
          tsv = item_class.to_csv(write_headers: true, col_sep:"\t")
          CSV.open('output.tsv', 'w') do |csv|
            CSV.parse(tsv) do | c |
              csv << c
            end
          end
        end
      end
    end
    
    SequelTsv::One.main
    

    输出:

    id  name    price
    1   abc 39.307899453608364
    2   def 99.28471503410731
    3   ghi 58.0295131255661
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-19
      • 2020-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多