Rails SQL query builder… Or ActiveRecord query builder

后端 未结 3 1707
终归单人心
终归单人心 2020-12-19 11:21

I need to run sql query like

sql = \'SELECT * FROM users WHERE id != \' + self.id.to_s + \' AND id NOT IN (SELECT artner_id FROM encounters WHERE user_id =          


        
相关标签:
3条回答
  • 2020-12-19 11:47

    You need the squeel gem. It extends AR with blocks and makes very complicated queries with ease.

    Just few features:

    # 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%'))    
    

    Note the using blocks: {...} here aren't hashes. Also note the absence of symbols.

    If you decide to pick it, read the section that starts with "This carries with it an important implication"

    0 讨论(0)
  • 2020-12-19 11:50

    There's a ruby library that utilizes relational algebra. It is called ARel. If you are using Rails 3.x, then you already have.

    ids   = Partner.where(user_id: self.id).pluck(:partner_id) << self.id
    users = User.where("id NOT IN #{ ids.join(',') }")
    
    0 讨论(0)
  • 2020-12-19 12:09

    Here's the same query cast into rails AREL terms. It's not pretty yet -- it's a complicated query in general.

    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)
    

    (I've not tested this, so it's possible there could be typo's in it.)

    I'd recommend a couple things:

    When you have complex where clauses they can be chained together and AREL will put them back together generally pretty well. This allows you to use scopes in your model classes and chain them together.

    For example, you could do this:

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

    Then you could rewrite these portions of the query this way:

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

    and so on.

    Breaking the queries down into smaller parts also makes it easier to test specific pieces of it with your rspecs. It results in more modular, maintainable code.

    For more details, check out the Rails Guide - Active Record Query Interface

    0 讨论(0)
提交回复
热议问题