【问题标题】:Splitting the Rows of a MySQL Query Using Ruby and Writing to a CSV File使用 Ruby 拆分 MySQL 查询的行并写入 CSV 文件
【发布时间】:2016-06-07 03:43:27
【问题描述】:

使用 Ruby 从远程数据库自动执行 MySQL 查询,我希望根据下面找到的 month 查询的值拆分行。

这是为了根据开始日期为所有客户生成 2014 年 6 月的每周(周三至下周二)报告。虽然报告中的其他内容不会改变,但行的重复是基于该开始日期(在下面的 case 语句中解释)。

请注意此处使用 mysql2watircsv 宝石。

简化代码:

#!/usr/local/bin/ruby
require "mysql2"
require "watir"
require "csv"

puts "Initializing Report"

Mysql2::Client.default_query_options.merge!(:as => :array)

mysql = Mysql2::Client.new(:host => "1.2.3.4", :username => "user", :pass => "password", :database => "db")

puts "Successfully accessed db"

month = mysql.query("SELECT DATE_FORMAT(db.table.start, '%m') FROM db.table WHERE db.start.group = 1;")

day = mysql.query("SELECT DATE_FORMAT(db.table.start, '%d') FROM db.table WHERE db.start.group = 1;")

report = mysql.query("SELECT db.table.client, SELECT DATE_FORMAT(db.table.start, '%m/%d/%Y'), SELECT DATE_FORMAT(db.table.end, '%m/%d/%Y') FROM db.table WHERE db.start.group = 1;")

case month
when 5
  # code splitting one row into four
when 6
  if day <= 4
    # code splitting one row into four using weekOf
  elsif day >= 11 and day <= 17
    # code splitting one row into three using weekOf
  elsif day >= 18 and day <= 24
    # code splitting one row into two using weekOf
  else
    # no splitting; only one row using weekOf
  end
end

CSV.open("Report.csv", "wb") do |csv|
  csv << ["Week of", "Client", "Start Date", "End Date"]
  weekOf.zip(report).each {|row| csv << row.flatten}
end

puts "Results can be found in Report.csv"

当前输出(如果我要注释掉 case 语句,请删除 CSV 标头中的 "Week of", 并仅将 report 查询写入 CSV):

Client, Start Date, End Date
companyrecordlabel, 05/20/2014, 07/09/2015
beeUrself, 05/27/2014, 02/01/2016
overflowStack, 06/04/2014, 12/11/2015
chapoChaps, 06/11/2014, 01/16/2016
Meds4U, 06/18/2014, NULL
  .
  .
  .

我希望得到以下输出:

Week of, Client, Start Date, End Date
06/04/2014, companyrecordlabel, 05/20/2014, 07/09/2015
06/11/2014, companyrecordlabel, 05/20/2014, 07/09/2015
06/18/2014, companyrecordlabel, 05/20/2014, 07/09/2015
06/25/2014, companyrecordlabel, 05/20/2014, 07/09/2015
06/04/2014, beeUrself, 05/27/2014, 02/01/2016
06/11/2014, beeUrself, 05/27/2014, 02/01/2016
06/18/2014, beeUrself, 05/27/2014, 02/01/2016
06/25/2014, beeUrself, 05/27/2014, 02/01/2016
06/04/2014, overflowStack, 06/04/2014, 12/11/2015
06/11/2014, overflowStack, 06/04/2014, 12/11/2015
06/18/2014, overflowStack, 06/04/2014, 12/11/2015
06/25/2014, overflowStack, 06/04/2014, 12/11/2015
06/11/2014, chapoChaps, 06/11/2014, 01/16/2016
06/18/2014, chapoChaps, 06/11/2014, 01/16/2016
06/25/2014, chapoChaps, 06/11/2014, 01/16/2016
06/18/2014, Meds4U, 06/18/2014, NULL
06/25/2014, Meds4U, 06/18/2014, NULL
  .
  .
  .

为了清楚起见:"Client"companyrecordlabel 有四行,因为它的 "Start Date" 是在 5 月,而 "Client" Meds4U 只分成两行,因为它的 "Start Date" 是在 6 月 18 日.

【问题讨论】:

    标签: mysql ruby csv split row


    【解决方案1】:

    我根据几个假设为以下答案构建了 FULL 代码:

    • 没有DATE_FORMAT(db.table.end, '%m') = 6
    • 您希望所有 列出的公司按其所在的顺序排列(即 db.table.id)
    • 查询时间对您来说不是什么大问题
    • 您想要但不能或忘记包含一个名为 weekOf 的数组

    您的查询中似乎也出现了太多次SELECT 一词。即使对于与您提供的样本一样小的查询,您也可能希望将其分开并避免将其全部放在一行上:

    month = mysql.query("SELECT DATE_FORMAT(db.table.start, '%m')
        FROM db.table
        WHERE db.start.group = 1;")
    

    代替:

    month = mysql.query("SELECT DATE_FORMAT(db.table.start, '%m') FROM db.table WHERE db.start.group = 1;")

    现在是代码本身:

    #!/usr/local/bin/ruby
    require "mysql2"
    require "watir"
    require "csv"
    
    puts "Initializing Report"
    
    Mysql2::Client.default_query_options.merge!(:as => :array)
    
    mysql = Mysql2::Client.new(:host => "1.2.3.4", :username => "user", :pass => "password", :database => "db")
    
    puts "Successfully accessed db"
    
    date = mysql.query("SELECT DATE_FORMAT(db.table.start, '%m'),
      DATE_FORMAT(db.table.start, '%d')
      FROM db.table
      WHERE db.start.group = 1;")
    
    report = mysql.query("SELECT c, s, e FROM (SELECT * FROM (SELECT db.table.id
      db.table.client AS c,
      DATE_FORMAT(db.table.start, '%m/%d/%Y') AS s,
      DATE_FORMAT(db.table.end, '%m/%d/%Y') AS e
      FROM db.table
      WHERE db.start.group = 1
      UNION ALL
      SELECT db.table.id
      db.table.client AS c,
      DATE_FORMAT(db.table.start, '%m/%d/%Y') AS s,
      DATE_FORMAT(db.table.end, '%m/%d/%Y') AS e
      FROM db.table
      WHERE db.start.group = 1
      HAVING ((DATE_FORMAT(db.table.start, '%m') = 5) OR (DATE_FORMAT(db.table.start, '%d') <= 4))
      UNION ALL
      SELECT db.table.id
      db.table.client AS c,
      DATE_FORMAT(db.table.start, '%m/%d/%Y') AS s,
      DATE_FORMAT(db.table.end, '%m/%d/%Y') AS e
      FROM db.table
      WHERE db.start.group = 1
      HAVING ((DATE_FORMAT(db.table.start, '%m') = 5) OR (DATE_FORMAT(db.table.start, '%d') <= 11))
      UNION ALL
      SELECT db.table.id
      db.table.client AS c,
      DATE_FORMAT(db.table.start, '%m/%d/%Y') AS s,
      DATE_FORMAT(db.table.end, '%m/%d/%Y') AS e
      FROM db.table
      WHERE db.start.group = 1
      HAVING ((DATE_FORMAT(db.table.start, '%m') = 5) OR (DATE_FORMAT(db.table.start, '%d') <= 18))) AS alias
      ORDER BY db.table.id) AS alias2;")
    
    weekOf = []
    
    date.each do |mon, day|
      if mon === 5
        weekOf << "06/04/2014"
        weekOf << "06/11/2014"
        weekOf << "06/18/2014"
        weekOf << "06/25/2014"
      elsif mon === 6
        if (day.to_i <= 4)
          weekOf << "06/04/2014"
          weekOf << "06/11/2014"
          weekOf << "06/18/2014"
          weekOf << "06/25/2014"
        elsif ((day.to_i >= 11) && (day.to_i <= 17))
          weekOf << "06/11/2014"
          weekOf << "06/18/2014"
          weekOf << "06/25/2014"
        elsif ((day.to_i >= 18) && (day.to_i <= 24))
          weekOf << "06/18/2014"
          weekOf << "06/25/2014"
        else
          weekOf << "06/25/2014"
        end
      else
        puts "Error: #{mon} is before May"
      end
    end
    
    CSV.open("Report.csv", "wb") do |csv|
      csv << ["Week of", "Client", "Start Date", "End Date"]
      weekOf.zip(report).each {|row| csv << row.flatten}
    end
    
    puts "Results can be found in Report.csv"
    

    解释:

    我假设查询时间对您来说不是一个大问题,因为您看到您的示例查询相当小并且不包含 JOIN。如果您发现您的查询变得大于十个左右INNER JOIN(例如,每个表都有数十万个条目),那么这可能不再是您的最佳解决方案。

    这个解决方案有两个部分。

    第一个是使用UNION ALL从数据库本身复制行。这意味着重复整个查询并在下面添加条件以指定此重复发生的时间。

    这就是HAVING 子句的用武之地。使用UNION ALL 时,必须以这种方式使用HAVING 而不是WHERE;因为后者会导致 MySQL 出错。

    还请务必记住,作为子查询结果创建的每个 MySQL 表都必须有一个别名:aliasalias2。为了ORDER BY db.table.id(偏离我的一个假设),我使用的不是一个而是两个嵌套查询,然后只选择下一部分所需的列。

    最后,我将两个单独的monthday 组合在一起,而是将它们变成了一个date:迭代时将返回一个二维数组。

    第二:我创建了您可能想要但忘记包含的 weekOf 数组。

    然后我迭代了date,以便将正确的"06/#{day}/2014" 推入weekOf 数组。

    就是这样!我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-24
      • 2018-11-29
      • 2016-01-30
      • 1970-01-01
      • 2015-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多