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来实现这个。嗯,据我所知不是这样。不过,我会提出另一个建议作为答案,看看人们对它的看法。谢谢你们的解决方案,伙计们!!你能帮我找到如何按月份分组的方法吗?谢谢哇!我有点被这个解决方案迷住了。