mysql - ActiveRecord 连接和位置

标签 mysql ruby-on-rails ruby activerecord ruby-on-rails-3.1

我有三个模型 Company、Deal 和 Slot。它们关联为 Company has_many deals 和 Deal has_many slots。如果 A 公司的所有交易都已过期,则所有 A 公司都可能会过期。当交易的所有时段都已过期时,交易也已过期。

我写了一个作用域..

scope :expired,
  lambda { |within|
    self.select(
      'DISTINCT companies.*'
    ).latest(within).joins(
      :user =>{ :deals => :slots }
    ).where(
      "companies.spam = false AND deals.deleted_at IS NULL
        AND deals.spam = false AND slots.state = 1 
        OR slots.begin_at <= :time",
      :time => Time.zone.now + SLOT_EXPIRY_MARGIN.minutes
    )
  }

从我想要实现的目标来看,上述范围对我来说似乎不合适。我需要所有交易的所有广告位都处于状态 1 或 begin_at 小于 :time 的公司使其过期。

感谢您提前查看。

最佳答案

AND 的优先级高于 SQL 中的 OR,因此您的 where 实际上会像这样解析:

(
        companies.spam = false
    and deals.deleted_at is null
    and deals.spam = false
    and slots.state = 1
)
or slots.begin_at <= :time

例如(为简洁起见略微删减):

mysql> select 1 = 2 and 3 = 4 or 5 = 5;
+---+
| 1 |
+---+

mysql> select (1 = 2 and 3 = 4) or 5 = 5;
+---+
| 1 |
+---+

mysql> select 1 = 2 and (3 = 4 or 5 = 5);
+---+
| 0 |
+---+

此外,您可能希望在 SQL 中使用占位符而不是文字 false,如果您想切换数据库,这应该会使事情变得更容易(当然,数据库可移植性在很大程度上是一个神话所以这只是一个建议);您也可以只在 SQL 中使用 not。此外,using a class method is the preferred way to accept arguments for scopes .使用 scoped 而不是 self 也是一个好主意,以防其他作用域已经在起作用,但如果您使用类方法,则不必担心。

如果我们用一些括号修复您的 SQL 中的分组,请为 false 使用占位符,并切换到类方法:

def self.expired(within)
  select('distinct companies.*').
  latest(within).
  joins(:user => { :deals => :slots }).
  where(%q{
        not companies.spam
    and not deals.spam
    and deals.deleted_at is null
    and (slots.state = 1 or slots.begin_at <= :time)
  }, :time => Time.zone.now + SLOT_EXPIRY_MARGIN.minutes)
end

如果你更喜欢小的 SQL block 而不是大的 SQL block ,你也可以这样写:

def self.expired(within)
  select('distinct companies.*').
  latest(within).
  joins(:user => { :deals => :slots }).
  where('not companies.spam').
  where('not deals.spam').
  where('deals.deleted_at is null').
  where('slots.state = 1 or slots.begin_at <= :time', :time => Time.zone.now + SLOT_EXPIRY_MARGIN.minutes)
end

这也巧妙地回避了您的“缺少括号”问题。


更新:根据评论中的讨论,我认为您正在寻找这样的东西:

def self.expired(within)
  select('distinct companies.*').
  latest(within).
  joins(:user => :deals).
  where('not companies.spam').
  where('not deals.spam').
  where('deals.deleted_at is null').
  where(%q{
      companies.id not in (
          select company_id
          from slots
          where state     = 1
            and begin_at <= :time
          group by company_id
          having count(*) >= 10
      )
  }, :time => Time.zone.now + SLOT_EXPIRY_MARGIN.minutes
end

底部的那个肮脏的东西捕获了所有有十个或更多过期或使用槽的公司 ID,然后 companies.id not in (...) 将它们从最终结果集中排除.

关于mysql - ActiveRecord 连接和位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9708577/

相关文章:

ruby-on-rails - Rake:记录任何正在执行的任务

php - 使用 mysql ajax codeigniter 的自动完成搜索不起作用

mysql - 如何在没有任何where条件的情况下使用like查询?

mysql - MySQL大数据量如何进行关键字查询?

ruby-on-rails - elm 将 json 发布到 Rails

ruby-on-rails - 将 LIKE 条件添加到 Rails Conditions block

ruby-on-rails - Rails - 从 has_many => 通过关联作为字符串获取名称

ruby-on-rails - 向 Rails API 发出 POST 请求时如何格式化日期字段

mySQL 在 1000A 和 2000A 之间选择

ruby-on-rails - Rails act_as_paranoid-belongs_to 不使用 with_deleted