【问题标题】:Rails SQL query builder... Or ActiveRecord query builderRails SQL 查询生成器...或 ActiveRecord 查询生成器
【发布时间】:2021-02-04 19:53:53
【问题描述】:

我需要像

一样运行 sql 查询
sql = 'SELECT * FROM users WHERE id != ' + self.id.to_s + ' AND id NOT IN (SELECT artner_id FROM encounters WHERE user_id = ' + self.id.to_s + ')'
sql += ' AND id NOT IN (SELECT user_id FROM encounters WHERE partner_id = ' + self.id.to_s + ' AND predisposition = ' + Encounter::Negative.to_s + ')'
sql += ' AND cfg_sex = ' + self.sex.to_s + ' AND cfg_country = ' + self.country.to_s + ' AND cfg_city = ' + self.city.to_s
sql += ' ORDER BY rand() LIMIT 1'

可以通过AR.find_by_sql执行,但是之前的代码可读性差。 是否有任何查询生成器可以构建该查询?

例如,Kohana(它是 PHP 框架,我是 php 开发人员,但我想将那种儿童语言更改为 ruby​​/rails)有一个查询构建器,其工作方式如下:

$sql = DB::select('*')->from('users');
$sql->where('id', 'NOT_IN', DB::expr('SELECT partner_id FROM encounters WHERE user_id = '.$user->id));
$sql->where('id', 'NOT_IN', DB::expr('SELECT user_id FROM encounters WHERE partner_id = '.$user->id.' AND predisposition = '.Encounter::Negative));
....
etc
...

使用 Kohana 查询构建器等查询构建器构建的查询更具可读性和可理解性。

有什么 gem 可以解决这个问题吗?

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-3 activerecord gem


    【解决方案1】:

    您需要squeel gem。它用块扩展 AR,并轻松进行非常复杂的查询。

    只有几个功能:

    # not_in == cool! )
    Product.where{id.not_in LineItem.select{product_id}}
    # SELECT "products".* FROM "products" WHERE "products"."id" NOT IN 
    # (SELECT "line_items"."product_id" FROM "line_items" )
    
    # outer joins on pure Ruby:
    LineItem.joins{product.outer}
    # LineItem Load (0.0ms)  SELECT "line_items".* FROM "line_items" 
    # LEFT OUTER JOIN "products" ON "products"."id" = "line_items"."product_id"
    
    # calcs, aliasing:
    Product.select{[avg(price).as(middle)]}
    # SELECT avg("products"."price") AS middle FROM "products"
    
    # comparison
    Product.where{id != 100500}
    Product.where{price<10}
    
    # logical OR
    Product.where{(price<10) | (title.like '%rails%')}
    # SELECT "products".* FROM "products" WHERE (("products"."price" < 10 OR
    # "products"."title" LIKE '%rails%'))
    
    # xxx_any feature (also available xxx_all)
    Product.where{title.like_any %w[%ruby% %rails%]}
    # SELECT "products".* FROM "products" WHERE (("products"."title" LIKE '%ruby%' OR 
    # "products"."title" LIKE '%rails%'))    
    

    注意使用块:{...} 这里不是哈希。还要注意没有符号。

    如果您决定选择它,请阅读以“这具有重要意义”开头的部分

    【讨论】:

      【解决方案2】:

      有一个使用关系代数的 ruby​​ 库。它被称为ARel。如果您使用的是 Rails 3.x,那么您已经拥有了。

      ids   = Partner.where(user_id: self.id).pluck(:partner_id) << self.id
      users = User.where("id NOT IN #{ ids.join(',') }")
      

      【讨论】:

      • 它可以在查询中粘贴数据库表达式,以构建类似'SELECT * FROM users WHERE id != ' + self.id.to_s + ' AND id NOT IN (SELECT artner_id FROM 遇到 WHERE user_id = ' + self.id.to_s + ')'?
      • 在这种情况下为什么不做左连接?
      【解决方案3】:

      这是转换为 rails AREL 术语的相同查询。它还不是很漂亮——总的来说这是一个复杂的查询。

      User.where("id = ? AND "
                 "id NOT IN (SELECT artner_id FROM encounters WHERE user_id = ?) AND " +  
                 "id NOT IN (SELECT user_id FROM encounters WHERE partner_id = ? AND predisposition = ? ) AND " + 
                  "cfg_sex = ? AND cfg_country = ? AND cfg_city = ?)", 
                  self.id, self.id, self.id, Encounter::Negative, 
                  self.sex, self.country, self.city).order(" rand() ").limit(1)
      

      (我没有测试过这个,所以可能有错字。)

      我推荐几件事:

      当您有复杂的 where 子句时,可以将它们链接在一起,并且 AREL 通常会将它们重新组合在一起。这允许您在模型类中使用范围并将它们链接在一起。

      例如,您可以这样做:

      class User < ActiveRecord::Base
        def self.in_city_state_country(city, state, country)
          where("cfg_sex = ? AND cfg_country = ? AND cfg_city = ?", city, state, country)
        end
      
        def self.is_of_sex(sex)
          where("cfg_sex = ?", sex)
        end
      end
      

      然后你可以用这种方式重写查询的这些部分:

      User.is_of_sex(user.sex).in_city_state_country(user.city, user.state, user.country)
      

      等等。

      将查询分解成更小的部分还可以更轻松地使用您的 rspec 测试其中的特定部分。它产生了更加模块化、可维护的代码。

      更多详情,请查看Rails Guide - Active Record Query Interface

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-21
        • 1970-01-01
        • 1970-01-01
        • 2011-11-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多