【问题标题】:Writing a Rails migration to collapse booleans into an enum编写 Rails 迁移以将布尔值折叠成枚举
【发布时间】:2019-12-03 05:42:53
【问题描述】:

我在对数据建模时犯了一个错误,并创建了一个包含 4 个布尔值的模型,一次只有一个处于活动状态。我想创建一个 Postgres 枚举类型的列,然后根据之前设置的布尔标志设置值。

我知道我想从这样的事情开始:

class ChangePositionTypeToBeEnumInPosition < ActiveRecord::Migration[6.0]
  def change

    reversible do |change|
      change.up do
        execute <<-SQL
          CREATE TYPE position_type AS ENUM ('chair', 'jboard', 'eboard', 'aboard');
        SQL

        # TODO: Execute code to create a new column and set values based off of existing values in the same row
        # TODO: Drop the 4 boolean columns
      end

      change.down do
        # TODO: Create the 4 boolean columns
        # TODO: Set one to true depending on the enum state

        execute <<-SQL
          DROP TYPE position_type;
        SQL
      end
    end
  end
end

我的问题是:我可以在我的 TODO cmets 所在的地方做什么?之前我在我的用户模型中编写了一个从整数到 Postgres 枚举的迁移,我的 change_column 代码如下所示:

        change_column :users, :member_type, <<-SQL.strip
          member_status USING 
          CASE member_type
                WHEN '0'  THEN 'inactive'::member_status
                WHEN '1'  THEN 'active'::member_status
                WHEN '2'  THEN 'suspended'::member_status
          END
        SQL

我想我正在尝试编写的代码是此代码的一些变体,带有一连串的 if 语句。任何意见将不胜感激。

【问题讨论】:

  • 您想使用 postgres 枚举还是 rails 枚举?我们通常使用 rails 枚举而不是正确的 postgres 枚举类型。

标签: sql ruby-on-rails postgresql rails-migrations ruby-on-rails-6


【解决方案1】:

虽然您自己提出了有问题的方法,但无论如何这里是解决方案。

基于this article。您可以尝试以下方法:

class ChangePositionTypeToBeEnumInPosition < ActiveRecord::Migration[6.0]
  def change

    reversible do |change|
      change.up do
        execute <<-SQL
          CREATE TYPE position_type_enum AS ENUM ('chair', 'jboard', 'eboard', 'aboard');
        SQL
        add_column :positions, :position_type, :position_type_enum
        # You may also want to add index on this column

        # If this table's size is huge(10m-20m+), this is not a good idea in that case.
        execute <<-SQL
          WITH cte AS
          ( 
            SELECT  id,
                    CASE
                      WHEN chair = true THEN 'chair'::position_type_enum
                      WHEN jboard = true THEN 'jboard'::position_type_enum
                      WHEN eboard = true THEN 'eboard'::position_type_enum
                      ELSE 'aboard'::position_type_enum
                    END AS position_type
            FROM positions
          )
          UPDATE positions p
          SET position_type = c.position_type
          FROM cte c
          WHERE p.id = c.id;
        SQL

        remove_column :positions, :chair, :boolean
        remove_column :positions, :jboard, :boolean
        remove_column :positions, :eboard, :boolean
        remove_column :positions, :aboard, :boolean
      end

      change.down do
        add_column :positions, :chair, :boolean
        add_column :positions, :jboard, :boolean
        add_column :positions, :eboard, :boolean
        add_column :positions, :aboard, :boolean

        execute <<-SQL
          UPDATE positions
          SET chair = CASE WHEN position_type = 'chair' THEN true ELSE false END,
              jboard = CASE WHEN position_type = 'jboard' THEN true ELSE false END,
              eboard = CASE WHEN position_type = 'eboard' THEN true ELSE false END,
              aboard = CASE WHEN position_type = 'aboard' THEN true ELSE false END
        SQL

        remove_column :positions, :position_type
        execute <<-SQL
          DROP TYPE position_type_enum;
        SQL
      end
    end
  end
end

我没有尝试运行此代码,您应该更正您可能发现的任何拼写错误(并在此处发表评论,以便我更新答案)。

请阅读我在上面链接的文章,了解如何在模型上正确使用 postgres 枚举。

【讨论】:

  • 谢谢!我纠正了一些错别字,它就像一个魅力。我不知道cte 是什么所以我研究了一下,它正是这种情况下的正确工具。我已继续编辑您的帖子以包含更新后的答案 - 现在正在接受同行评审。
  • 那些更新被拒绝@NeilJohari,如果你能指出什么是错字,我可以解决这个问题。很高兴知道它有效。
  • 我想知道为什么它被拒绝了。 “FROM 位置”处的分号;必须删除第一个 SQL 语句中的逗号,并且也必须删除第二个 SQL 语句中的“END”处的逗号。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-19
  • 1970-01-01
  • 2023-03-07
相关资源
最近更新 更多