【问题标题】:Rails INNER JOIN expecting field "id" that does not yet existRails INNER JOIN 期待尚不存在的字段“id”
【发布时间】:2015-01-12 21:36:13
【问题描述】:

简短版:我需要使用自定义内部联接来查找一个表中的 id,其中两个字段在 Rails 中的两个表中都匹配,但是 Rails 内部联接对我不知道如何解决的关系做出假设。

长版:我接手了一个具有非规范化数据库的 Rails 项目。我将一些信息提取到一个新表中,现在我需要遍历每个条目并在原始表上放置一个外键,但我无法提出正确的 ActiveRecord 查询来获取信息。

在这种情况下,一个集合属于一个存储库,并且存储库可能有很多集合。

该项目最初将所有集合和存储库存储在同一个表中,如果它是存储库,则集合名称为 NULL,如果它是集合,则具有存储库名称和集合名称。我创建了一个迁移,它采用了所有唯一的存储库名称并将它们保存在存储库表中。现在我需要做的是返回集合表并根据字符串 repname 从匹配的存储库中添加 id。

我在 SQL 中有一个查询可以提取正确的数据:

ActiveRecord::Base.connection.execute("SELECT r.id, r.repname FROM collections c, repositories r WHERE c.repname = r.repname")

但是,我在与该对象交互时遇到了问题(出现诸如“#Mysql2::Result... 的未定义方法'last'之类的错误”),我希望我知道所有内容都已正确设置在Rails 模型。

当我尝试这个时,我得到一个错误:

Repository.joins(:collections).where("collections.repname = repositories.repname")
Repository Load (1.0ms)  SELECT `repositories`.* FROM `repositories` INNER JOIN `collections` 
ON `collections`.`repository_id` = `repositories`.`id` WHERE (collections.repname = repositories.repname)

Mysql2::Error: Unknown column 'collections.repository_id' in 'on clause': SELECT `repositories`.* FROM `repositories` INNER JOIN `collections` ON `collections`.`repository_id` = `repositories`.`id` WHERE (collections.repname = repositories.repname)
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'collections.repository_id' in 'on clause': SELECT `repositories`.* FROM `repositories` INNER JOIN `collections` ON `collections`.`repository_id` = `repositories`.`id` WHERE (collections.repname = repositories.repname)

我是 Rails 和 SQL 的新手,所以我认为我的问题实际上可能在于我如何设置模型。我使用的是数据库现有的命名约定...也许我应该改用 Rails repository_id 约定?

class Collection < ActiveRecord::Base
  has_many :archive_object_collections, :foreign_key => :collid, :primary_key => :collid
  has_many :archive_object, :through => :archive_object_collections
  belongs_to :repository, :foreign_key => "repid"
end

class Repository < ActiveRecord::Base
  has_many :collections
end

我担心切换到 repository_id 并不能解决问题,但是,因为 collections 表上还没有任何类型的 id。这就是为什么我需要运行这个查询,这样我就可以在集合表中找到并插入该信息作为外键。我在 Collections 类中尝试了没有 foreign_key 的内部连接查询,没有任何变化。

对于这个冗长而曲折的问题,我们深表歉意。有人有什么建议吗?谢谢!

【问题讨论】:

    标签: sql ruby-on-rails rails-activerecord


    【解决方案1】:

    我认为你应该在存储库模型中定义 foreign_key

    class Repository < ActiveRecord::Base
      has_many :collections, :primay_key => 'somekey',  :foreign_key => 'repid'
    end
    

    然后您可以运行一个简单的连接查询来根据您的查询获取结果。

    Repository.select('repositories.*, collections.*').where("repositories.col1 = ? AND collections.col2 = ?", val1, val2)
    

    否则,如果您想摆脱这些类型的键定义,那么最好开始使用 rails 约定。将外键定义为 model_id。在这种情况下,您不必定义键名。

    【讨论】:

    • 感谢foreign_key的建议!这让我更接近了。当我尝试您的查询时,它会创建以下 SQL: SELECT repositories.* FROM repositories WHERE (repositories.repname = 'First Church of Christ, Scientist, Boston, MA' AND collections.repname = 'First Church of Christ,科学家,马萨诸塞州波士顿')由于集合未添加到“FROM”部分,因此会出错。我认为真正的问题是我的外键目前是空的。我需要根据 SELECT r.id, r.repname FROM collections c, repositories r WHERE c.repname = r.repname 之类的结果来填充它
    • 您可以发布您的查询吗?
    • 2.1.3 :005 > Repository.where("repositories.repname = ? AND collections.repname = ?", val, val) 存储库负载 (2.9ms) SELECT repositories.* FROM @ 987654326@ WHERE (repositories.repname = 'First ...MA' AND collections.repname = 'First...MA') Mysql2::Error: Unknown column 'collections.repname' in 'where 子句': SELECT repositories .* FROM repositories WHERE (repositories.repname = 'First ...MA' AND collections.repname = 'First...MA')
    • 我想我可能已经集思广益来解决这个问题。如果在迁移中我遍历所有存储库,然后遍历所有集合,当我找到匹配的名称时,我可以插入存储库 ID 作为集合的外键。它很乱,但它应该只需要在生产环境中运行一次!
    • 是的,你是对的。我错过了在查询中选择 col。现在已经修改了。检查它是否有效?
    【解决方案2】:

    我决定遍历 collections 和 repositories 表并手动匹配它。这就是我最终要做的:

    class AddForeignKeyToColl < ActiveRecord::Migration
      def up
        # Add a repid to the collections
        add_column :collections, :repository_id, :integer
    
        # Need to go through all of the repositories
        # and match their name against the repname of the collections
        # Then, if they match, add the repository id to the collection as repid
        repos = Repository.all
        colls = Collection.all
        repos.each do |repo|
          rname = repo.repname
          colls.each do |coll|
            if coll.repname == rname
              coll.update_attributes(:repository_id => repo.id)
            end
          end
        end
      end
    
      def down
        raise ActiveRecord::IrreversibleMigration
      end
    end
    

    好像成功了!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-17
      • 1970-01-01
      • 2020-04-03
      • 2013-07-30
      相关资源
      最近更新 更多