簡體   English   中英

連接三個表(2級)並根據條件查找總和 - SQL Rails

[英]Joining three tables (2 level) and finding sum based on conditions - SQL Rails

我有以下架構:

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 |
-----------------------------------

我在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給錯誤“tasks.classification”是一個未知的列。 查看生成的查詢是可以理解的:

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|
-----------------------------------------------

我嘗試過group(user_id, MONTH(date))但結果很奇怪。 如何獲取此類信息?

順便說一句,將連接更改為:

joins(:timesheets, :timesheets => [:task, :timesheet_items])

解決了列找不到的問題:)

我終於到了這個解決方案。 任何優化的想法?

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'

我會做這樣的事情:

#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定義,你可以只調用@user.billable_hours@user.non_billable_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

然后 ...

class User < ActiveRecord::Base

  has_one :user_billing_metric, :inverse_of => :user

  delegate :billable_hours  , :to => :user_billing_metric
  delegate :unbillable_hours, :to => :user_billing_metric

  def read_only?
    true
  end
end

然后你可以:

u = User.find( ...)

u.billable_hours

... 要么 ...

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

等等

可能在那里做了一個愚蠢的錯字。

一個很好的功能是你可以,例如:

users_to_fire = User.joins(:user_billing_metric).
                     where(:user_billing_metrics => {:billable_hours = 0})

再一次,愚蠢的錯字可能。

將其推送到數據庫是非常有效的,遠比嘗試通過Rails運行它更有效。 一個不錯的查詢優化器不會在視圖中評估不需要的表達式,甚至不會執行邏輯冗余的連接。

無論如何,只是浮出來征求意見。 我知道將業務邏輯放在數據庫層並不符合每個人的口味,但它會讓它干澀,我在自己的應用程序中有情況,出於性能原因,它絕對是唯一的選擇。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM