Mysql 连接三个表(2级)并根据条件查找和-SQL Rails

Mysql 连接三个表(2级)并根据条件查找和-SQL Rails,mysql,ruby-on-rails,join,Mysql,Ruby On Rails,Join,我有以下模式: User: --- ID --- Tasks: ------------------- ID | classification ------------------- Timesheets: ------------------------ ID | task_id | user_id ------------------------ TimesheetItem: -------------------------------- ID | timesheet_id |

我有以下模式:

User:
---
ID
---

Tasks:
-------------------
ID | classification
-------------------

Timesheets:
------------------------
ID  |  task_id | user_id
------------------------

TimesheetItem:
--------------------------------
ID | timesheet_id | hours | date
--------------------------------
协会:

class User 
  has_many :timesheets
end

class Task 
  has_many :timesheets
end

class Timesheet 
  has_many :timesheet_items
  belongs_to :user
  belongs_to :task
end

class TimesheetItem
  belongs_to :timesheet
end
分类可以是计费的,也可以是不计费的。 现在,我需要为每个用户找到计费小时数和非计费小时数之和,如下所示:

-----------------------------------
|user_id | billable | nonbillable |
-----------------------------------
#id
class User 
  has_many :timesheets
  has_many :tasks, :through => :timesheets

  def billable_hours
    self.tasks.billable.collect{|task| task.timesheet_items_for(self)}.flatten.collect(&:hours).sum
  end

  def non_billable_hours
    self.tasks.non_billable.collect{|task| task.timesheet_items_for(self)}.flatten.collect(&:hours).sum
  end  
end

#id, classification
class Task 
  has_many :timesheets

  def timesheet_items_for(user)
    self.timesheets.for_user(user).collect(&:timesheet_items).flatten
  end

  named_scope :billable, :conditions => ["classification = ?", "Billable"]
  named_scope :non_billable, :conditions => ["classification = ?", "Non-Billable"]
end

#id, task_id, user_id
class Timesheet 
  has_many :timesheet_items
  belongs_to :user
  belongs_to :task

  named_scope :for_user, lambda {|user| {:conditions => ["user_id = ?", user.id]} }
end

#id, timesheet_id, hours
class TimesheetItem
  belongs_to :timesheet
end
我在Rails中所做的是:

User.joins(:timesheets, :timesheets => :task, :timesheets => :timesheet_items)
    .select("SUM(CASE 
                   WHEN tasks.task_classification = 'Billable' 
                      THEN timesheet_items.hours 
                   ELSE 
                      0 
                 END) as billable, 
             SUM(CASE 
                   WHEN tasks.task_classification = 'Non-Billable' 
                     THEN timesheet_items.hours 
                   ELSE 0 
                 END) as nonbillable")
但是MySQL给出了错误任务。分类是未知列。查看正在生成的查询,这是可以理解的:

SELECT SUM(CASE WHEN tasks.classification = 'Billable' THEN hours ELSE 0 END) as billable, SUM(CASE WHEN tasks.classification = 'Non-Billable' THEN hours ELSE 0 END) as nonbillable FROM `users` INNER JOIN `timesheets` ON `timesheets`.`user_id` = `users`.`id` INNER JOIN `timesheet_items` ON `timesheet_items`.`timesheet_id` = `timesheets`.`id`
正如您所看到的,任务表没有被联接

我如何做到这一点?谢谢

编辑:

我使用了一个简单的sql查询,它加入了tasks表以获得结果,因为这些数据只在一个地方使用,很少使用

但现在我需要按月对小时数进行分组,并找出每个用户为计费小时数和非计费小时数记录的小时数。例如

结果:

-----------------------------------------------
user_id | month | BillableHrs | NonBillableHRS|
-----------------------------------------------
我尝试了groupuser_id,MONTHdate,但是。。结果很奇怪。我怎样才能得到这样的信息呢

顺便说一句,将连接更改为:

加入:时间表,:时间表=>[:任务,:时间表\u项]

解决了列未找到的问题:

我终于找到了这个解决办法。有优化的想法吗

SELECT 
  users.id as user_id,
  users.name as user_name,
  CONCAT(MONTHNAME(date)," ",YEAR(date)) as month,
  SUM( CASE 
        WHEN tasks.task_classification = "Billable" 
          THEN hours 
        ELSE 
          0 
        END ) as blb_sum, 
  SUM( CASE 
        WHEN tasks.task_classification = "Non-Billable"
          THEN hours 
        ELSE 
          0 
        END ) as nblb_sum
FROM `users` 
  INNER JOIN `timesheets` ON `timesheets`.`user_id` = `users`.`id` 
  INNER JOIN `timesheet_items` ON `timesheet_items`.`timesheet_id` = `timesheets`.`id` 
  INNER JOIN `tasks` ON `timesheets`.`task_id` = `tasks`.`id` 
WHERE 
  timesheet_items.date >= '2013-11-1' AND
  timesheet_items.date <= '2013-11-31'

我会这样做:

-----------------------------------
|user_id | billable | nonbillable |
-----------------------------------
#id
class User 
  has_many :timesheets
  has_many :tasks, :through => :timesheets

  def billable_hours
    self.tasks.billable.collect{|task| task.timesheet_items_for(self)}.flatten.collect(&:hours).sum
  end

  def non_billable_hours
    self.tasks.non_billable.collect{|task| task.timesheet_items_for(self)}.flatten.collect(&:hours).sum
  end  
end

#id, classification
class Task 
  has_many :timesheets

  def timesheet_items_for(user)
    self.timesheets.for_user(user).collect(&:timesheet_items).flatten
  end

  named_scope :billable, :conditions => ["classification = ?", "Billable"]
  named_scope :non_billable, :conditions => ["classification = ?", "Non-Billable"]
end

#id, task_id, user_id
class Timesheet 
  has_many :timesheet_items
  belongs_to :user
  belongs_to :task

  named_scope :for_user, lambda {|user| {:conditions => ["user_id = ?", user.id]} }
end

#id, timesheet_id, hours
class TimesheetItem
  belongs_to :timesheet
end

然后在您的控制器中,假设您有@user defined,您可以只调用@user.billable\u hours或@user.non\u billable\u hours

因为这是一些非常难看的代码,如果它被表示为activerecord和SQL的混合体,一个想法是将复杂查询定义为一个视图,并将其引用为一个模型

这非常简单-如果这是用户级别的一组度量,那么按照以下行构造一个视图:

create view user_billing_metrics
as
select  user.id user_id,
        sum(case ... blah blah) billable_hours,
        sum(case ... blah blah) unbillable_hours
from    ...
然后创建一个只读模型

class UserBillingMetric < ActiveRecord::Base

  belongs_to :user, :inverse_of => :user_billing_metric

  def read_only?
    true
  end

end
。。。或者

u = User.find( ...)
hours= u.user_billing_metric
等等

可能有个愚蠢的打字错误

其中一个很好的特点是,您可以,例如:

users_to_fire = User.joins(:user_billing_metric).
                     where(:user_billing_metrics => {:billable_hours = 0})
同样,愚蠢的打字错误也是可能的

将其下推到数据库非常有效,比通过Rails运行更有效。一个好的查询优化程序不会计算视图中不需要的表达式,甚至不会执行逻辑上冗余的连接


不管怎么说,只是把这个浮出水面征求意见。我知道,将业务逻辑放在数据库层并不符合每个人的口味,但它可以让它保持干爽,而且在我自己的应用程序中,出于性能原因,它绝对是唯一的选择。

这是activerecord和SQL的丑陋组合-我只是将其作为SQL语句运行。哈哈。是的,你说得对。我正在考虑这样做。但AR中有什么方法可以解决这个问题吗?顺便说一句,我将使用纯SQL来实现这个。嗯,据我所知不是这样。不过,我会提出另一个建议作为答案,看看人们对它的看法。谢谢你们的解决方案,伙计们!!你能帮我找到如何按月份分组的方法吗?谢谢哇!我有点被这个解决方案迷住了。