如何将嵌套哈希提取到数据库表中?

Posted

技术标签:

【中文标题】如何将嵌套哈希提取到数据库表中?【英文标题】:How to extract nested hashes into database table? 【发布时间】:2014-01-20 12:14:40 【问题描述】:

我正在尝试从 Mailchimp 中提取一些以嵌套哈希返回的数据。这是我得到的结果的精简版。对于每封电子邮件,有多个 GROUPINGS,对于每个 GROUPING,有多个 GROUPS。

我的目标是将其放入一个 mysql 表中,其布局如下:email_addr、list、grouping1_id、grouping1_name、group1_name、group1_interest、group2_name、group2_interest、grouping2_id、grouping2_name 等。所以每个订阅者只有一行,所有分组和分组信息。

"email"=>"dummy@gmail.com", "merges"=>"EMAIL"=>"dummy@gmail.com", 
"GROUPINGS"=>["id"=>1, "name"=>"Grouping One", "groups"=>["name"=>"Group One", 
"interested"=>false, "name"=>"Group", "interested"=>true, 
"name"=>"Group Three", "interested"=>true], "id"=>2, "name"=>"Grouping Two", 
"groups"=>["name"=>"Group Four", "interested"=>false, 
"name"=>"Group Five", "interested"=>false]]

现在,我在下面的代码运行并将嵌套块的结果插入到表中,但每次通过 groups.each_with_index 语句都有一行。到目前为止,我的方法似乎过于复杂,但我不确定如何正确处理数据。

感谢任何帮助。

更新: 我稍微清理了逻辑并将数据库写入分离到哈希处理的每个级别。现在数据已正确插入和更新到数据库中。虽然这样还是觉得很不雅。

def organize_members_subs
  @members_data = @members_subs["data"]
  @members_data.each do |member|
    @email_addr = member["email"]
    @db.query("INSERT INTO db.details
        (email_addr, list)
        VALUES  ('#@email_addr', '#@list' ) ")

    groupings = member["merges"]["GROUPINGS"]
    groupings.each_with_index do |grouping, index|
      @groupings_name = grouping["name"]
      @groupings_id = grouping["id"]
      @groupings_label = "grp#index_"
      @db.query("UPDATE db.details
        SET grouping#index_id = '#@groupings_id'
          , grouping#index_name = '#@groupings_name'
        WHERE email_addr = '#@email_addr' ")

      groups = member["merges"]["GROUPINGS"][index]["groups"] 
      groups.each_with_index do |group, index|
        @group_name = group["name"]
        @group_interested = group["interested"]
        @db.query("UPDATE db.details
        SET #@groupings_labelgroup#index_name = '#@group_name'
          , #@groupings_labelgroup#index_int = '#@group_interested'
        WHERE email_addr = '#@email_addr' ")

        break if index == groups.length
      end
      break if index == groupings.length
    end
  end
end

【问题讨论】:

首先,让@groupingsXname@groupingsX_id 成为一个哈希数组怎么样,这样您就可以用@groupings[index] = "name" => grouping["name"], "id" => grouping["id"] 替换这六个分配,或者只是让grouping 成为一个哈希数组?另外,考虑使用:name:id,而不是"name""id" 作为键。最后,将"email"=>"email",... 放在多行中会很有帮助,并带有行继续字符\,因此读者不必水平滚动即可阅读该行。 @CarySwoveland 感谢您的建议。我做了一些更改,现在确实将数据正确写入数据库。不过,它似乎仍然可以改进。你有什么建议?如果您添加答案,我会接受。 你能澄清一下,第一个"email" => "email"...实际上是指一个电子邮件地址吗?所以在实际使用中会是"email" => "andy@example.com"....?我知道你已经缩减了这个例子,但是如果这是实际发生的事情,你可以使用虚拟电子邮件地址吗?我不熟悉 Mailchimp。 @Beartech 是的,你是对的。我的占位符选择不佳。我在示例中添加了一个虚拟电子邮件地址。 所以"email"=>"dummy@gmail.com", 可以被剥离以移除外部哈希?列是恒定的吗?即是否会有可变数量的分组?以及分组内的可变数量的组?另外,有没有机会为此使用 Rails? 【参考方案1】:

首先,我想仔细看看你的哈希值。我没有自己重新格式化,而是这样做了:

require "awesome_print"
h = `"email"=>..., "interested"=>false]]`
ap h

向下滚动到我的答案底部以查看 ap 的哈希格式。

假设 db 结构是给定的,我将回答您的问题,但想说明几点:

如果"id" 对于每条grouping 记录都是唯一的,您能否将其设为密钥,而无需使用index? 如果"name" 对于每条grouping 记录都是唯一的,您能否同时省略"id"index? 如果"name" 对于每条group 记录都是唯一的(对于给定的grouping),您能否只为每个组设置group["name"] => group["interested"]

继续您的代码,我还将假设您的哈希结构已给出。稍后,我将重新审视这个假设。

我对您的代码提出的更改相当小,有些纯粹是风格上的:

使所有实例变量成为局部变量,这意味着必须将两个附加参数传递给def organize_members_subs。 除了两个可能的例外,消除定义后只使用一次的局部变量。例如,而不是 groupings_id = grouping["id"],然后是SET grouping#index_id = '#@groupings_id',只有SET grouping#index_id = '#grouping["id"]'。 两个可能的例外是groupingsgroups。例如,您可以通过编写来摆脱前者 member["merges"]["GROUPINGS"].each_with_index do |grouping, index_1|。我会将它们保留为变量(这样我就可以轻松检查它们的值), 但这是一个风格决定。 groupings.each_with_index do |grouping, index| 中的变量index 在内部块的范围内,它使用同名的迭代器变量。 我认为后者优先,但它们应该以不同的方式命名。我已将它们分别更改为 index_outindex_inindex_out 的范围从 0groupings.length-1,因此 break if index_out == groupings.length 永远不会被执行,因此可能会被删除。 break if index_in == groups.length 同上。 我将groupings_label = "grp#index_" 向下移动以提醒人们注意这一事实,即仅在以后需要它,而不是在前面的SET 表达式中。

这些更改导致以下结果:

    def organize_members_subs(db, list, @members_subs["data"])
      members_data.each do |member|
        email_addr = member["email"]
        db.query("INSERT INTO db.details
            (email_addr, list)
            VALUES  ('#email_addr', '#list' ) ")
        groupings = member["merges"]["GROUPINGS"]
        groupings.each_with_index do |grouping, index_out|
          db.query("UPDATE db.details
          SET grouping#index_out_id   = '#grouping["id"]'
            , grouping#index_out_name = '#grouping["name"]'
          WHERE email_addr = '#email_addr' ")
          groupings_label = "grp#index_out_"

          groups = member["merges"]["GROUPINGS"][index_out]["groups"] 
          groups.each_with_index do |group, index_in|
            db.query("UPDATE db.details
            SET #groupings_labelgroup#index_in_name = '#group["name"]'
              , #groupings_labelgroup#index_in_int = '#group["interested"]'
            WHERE email_addr = '#email_addr' ")   
          end
        end
      end
    end

查看您的哈希,我想知道您是否可以将其简化为以下内容(格式由出色的打印提供):

  
      "email" => "dummy@gmail.com",
     "merges" => 
             "EMAIL" => "dummy@gmail.com",
         "GROUPINGS" => 
             1 => 
                   "name" => "Grouping One",
                 "groups" => 
                       "Group One" => false,
                       "Group Two" => true,
                     "Group Three" => true
                 
             ,
             2 => 
                   "name" => "Grouping Two",
                 "groups" => 
                     "Group Four" => false,
                     "Group Five" => false
                 
             
         
     
  

甚至


     "email" => "dummy@gmail.com",
    "merges" => 
            "EMAIL" => "dummy@gmail.com",
        "GROUPINGS" => 
            "Grouping One" => 
                  "Group One" => false,
                  "Group Two" => true,
                "Group Three" => true
            ,
            "Grouping Two" => 
                "Group Four" => false,
                "Group Five" => false
            
        
    

这些不是建议,而是值得深思的食物。

对您的哈希应用了很棒的打印:

ap h # =>
    
         "email" => "dummy@gmail.com",
        "merges" => 
                "EMAIL" => "dummy@gmail.com",
            "GROUPINGS" => [
                [0] 
                        "id" => 1,
                      "name" => "Grouping One",
                    "groups" => [
                        [0] 
                                  "name" => "Group One",
                            "interested" => false
                        ,
                        [1] 
                                  "name" => "Group",
                            "interested" => true
                        ,
                        [2] 
                                  "name" => "Group Three",
                            "interested" => true
                        
                    ]
                ,
                [1] 
                        "id" => 2,
                      "name" => "Grouping Two",
                    "groups" => [
                        [0] 
                                  "name" => "Group Four",
                            "interested" => false
                        ,
                        [1] 
                                  "name" => "Group Five",
                            "interested" => false
                        
                    ]
                
            ]
        
    

【讨论】:

【参考方案2】:

首先,也许是额外的,但我喜欢使用符号,因为我在 Rails 中做了很多工作。所以让我们从这里偷一个方法:How do I convert a Ruby hash so that all of its keys are symbols?

def recursive_symbolize_keys(h)
  case h
    when Hash
      Hash[
          h.map do |k, v|
            [ k.respond_to?(:to_sym) ? k.to_sym : k, recursive_symbolize_keys(v) ]
          end
      ]
    when Enumerable
      h.map  |v| recursive_symbolize_keys(v) 
    else
      h
  end
end

好的,让我们构建一个类,以便随着我们的需求变化而更容易操作和扩展:

class MemberSub
  attr_accessor :email, :groupings, :data_hash, :list, :data_row, :db_sql

  def initialize(data_hash)

    #convert all keys to symbols
    @data_hash = recursive_symbolize_keys(data_hash)
    @email = @data_hash[:email]
    @list = 'Members'
    @groupings = @data_hash[:merges][:GROUPINGS]
    @data_row  = data_row
    @db_sql = db_insert
  end

  def data_row
    #returns a data row for DB
    row_hash = 
    row_hash['email'] = @email
    row_hash['list'] = @list
    gc = 1
    #iterate through groupings
    @groupings.each_with_index do |grouping, index|
      row_hash["grouping#index + 1_id"] = grouping[:id]
      row_hash["grouping#index + 1_name"] = grouping[:name]
      #iterate through the groups
      grouping[:groups].each do |group|
        row_hash["group#gc_name"] = group[:name]
        row_hash["group#gc_interest"] = group[:interested]
        gc += 1
      end
    end
    row_hash
  end

  def db_insert
    "INSERT INTO db.details (#@data_row.keys) VALUES (#@data_row.values)".tr('[]','')
  end  

end

现在您可以使用任何迭代方法连续输入它并创建一个新对象:

row = MemberSub.new("email"=>"dummy@gmail.com", "list"=>"Members", "merges"=>  
"EMAIL"=>"dummy@gmail.com", "GROUPINGS"=>["id"=>1, "name"=>"Grouping One", "groups"=>  
["name"=>"Group One", "interested"=>false, "name"=>"Group Two", "interested"=>true,  
"name"=>"Group Three", "interested"=>true], "id"=>2, "name"=>"Grouping Two", "groups"=>  
["name"=>"Group Four", "interested"=>false, "name"=>"Group Five", "interested"=>false]])

并进行查询:

db.query(row.db_sql)

db.query(INSERT INTO db.details ("email", "list", "grouping1_id", "grouping1_name", 
"group1_name", "group1_interest", "group2_name", "group2_interest", "group3_name", 
"group3_interest", "grouping2_id", "grouping2_name", "group4_name", "group4_interest",
 "group5_name", "group5_interest") VALUES ("dummy@gmail.com", "Members", 1, "Grouping One",
 "Group One", false, "Group Two", true, "Group Three", true, 2, "Grouping Two", "Group Four",
 false, "Group Five", false))

其他方法应该是不言自明的。您不必将它们都作为attar_accessor 提供,但我只是这样做了。

【讨论】:

如果你想省略 recursive_symbolize_keys 方法,只需将其余代码中的符号替换为带引号的字符串即可。

以上是关于如何将嵌套哈希提取到数据库表中?的主要内容,如果未能解决你的问题,请参考以下文章

当嵌套表属于记录类型时,如何将数据填充到 Oracle 中的嵌套表中

将数据从 ref 游标提取到嵌套表中时出现不一致的数据类型错误

如何将 API 响应数据提取到 HTML 表中

如何将值插入到python中的嵌套表中?

无法将值存储到 oracle 中的嵌套表中

sql server如何如何从一个表中提取部分资讯插入到另一表中