質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Q&A

解決済

1回答

1552閲覧

関連テーブルの内容でグループ分けして集計

t.togashi

総合スコア22

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

0グッド

0クリップ

投稿2020/04/19 13:12

編集2020/04/20 12:59

前提・実現したいこと

Ruby on Railsで残業時間を管理するアプリを制作しています。
アプリにおいては、月毎の合計残業時間を、個人、及びグループ毎に集計しグラフにしたいと考えています。

該当のソースコード

schema.rb(本件に不要な属性を省略)

rb

1 create_table "overtimes", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| 2 t.date "date", null: false 3 t.bigint "user_id", null: false 4 t.integer "work_time_minute", null: false 5 t.index ["user_id", "date"], name: "index_overtimes_on_user_id_and_date", unique: true 6 t.index ["user_id"], name: "index_overtimes_on_user_id" 7 end 8 9 create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| 10   t.string "name", null: false 11 t.string "group", null: false 12 "index_users_on_reset_password_token", unique: true 13 end 14 15 add_foreign_key "overtimes", "users"

overtime.rb

rb

1class Overtime < ApplicationRecord 2 belongs_to :user 3 4 # 月毎の合計残業時間を個人毎に集計するクラスメソッド 5 def self.monthly_chart_data(userid) 6 # {[2019, 4]=>○○○○, [2019, 5]=>○○○○, …} を生成 7 year_and_month_minute_data = Overtime.where(user_id: userid).group("extract(year from date)").group("extract(month from date)").sum(:work_time_minute) 8 # {["2019年4月"=>○○○○, "2019年4月"=>○○○○, …} に成形 9 monthly_hour_data = {} 10 year_and_month_minute_data.each do |key, value| 11 monthly_hour_data["#{key[0].floor}#{key[1].floor}月"] = (value.to_f / 60).floor(1) 12 end 13 monthly_hour_data 14 end 15 16 17 18 # 月毎の合計残業時間をグループ毎に集計するクラスメソッド) 19 def self.group_monthly_chart_data 20  # {"A": {[2019, 4]=>○○○○, [2019, 5]=>○○○○, …}, "B": … } のようにまとめたいのですが... 21 group_year_and_month_minute_data = Overtime.joins(:user).select("overtimes*, users*").group(:group).group("extract(year from date)").group("extract(month from date)").sum(:work_time_minute) 22 end 23end

発生している問題

月毎の合計残業時間をまとめるクラスメソッドを、個人毎の場合は上記の通り作成できました。
一方グループ毎の場合は、上記のコードでは、以下のエラーメッセージが出ます。

pry(main)> Overtime.joins(:user).select("overtimes*, users*").group(:group).group("extract(year from date)").group("extract(month from date)").sum(:work_time_minute) (2.4ms) SELECT SUM(`overtimes`.`work_time_minute`) AS sum_work_time_minute, `group` AS group, extract(year from date) AS extract_year_from_date, extract(month from date) AS extract_month_from_date FROM `overtimes` INNER JOIN `users` ON `users`.`id` = `overtimes`.`user_id` GROUP BY `group`, extract(year from date), extract(month from date) ActiveRecord::StatementInvalid: Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'group, extract(year from date) AS extract_year_from_date, extract(month from dat' at line 1: SELECT SUM(`overtimes`.`work_time_minute`) AS sum_work_time_minute, `group` AS group, extract(year from date) AS extract_year_from_date, extract(month from date) AS extract_month_from_date FROM `overtimes` INNER JOIN `users` ON `users`.`id` = `overtimes`.`user_id` GROUP BY `group`, extract(year from date), extract(month from date)

試した事

Overtimeのクラスメソッドとして記述した結果、上記のようにエラーが出たため、Userのクラスメソッドとして記述した場合はどうなのかを試してみました。
すると、以下のように望んでいる形に近しい結果が得られました。

pry(main)> User.joins(:overtimes).select("overtimes*, users*").group(:group).group("extract(year from date)").group("extract(month from date)").sum(:work_time_minute) (4.0ms) SELECT SUM(work_time_minute) AS sum_work_time_minute, `users`.`group` AS users_group, extract(year from date) AS extract_year_from_date, extract(month from date) AS extract_month_from_date FROM `users` INNER JOIN `overtimes` ON `overtimes`.`user_id` = `users`.`id` GROUP BY `users`.`group`, extract(year from date), extract(month from date) => {["A", 2019, 4]=>1828, ["A", 2019, 5]=>6418, ["A", 2019, 6]=>6247, ["A", 2019, 7]=>4820, ["A", 2019, 8]=>4033, ["A", 2019, 9]=>6835, ["A", 2019, 10]=>6717, ["A", 2019, 11]=>5469, ["A", 2019, 12]=>4882, ["A", 2020, 1]=>4365, ["A", 2020, 2]=>6504, ["A", 2020, 3]=>5693, ["A", 2020, 4]=>2593, ["B", 2019, 4]=>1414, ["B", 2019, 5]=>4339, ["B", 2019, 6]=>5215, ["B", 2019, 7]=>3992, ...

データの性格上、UserではなくOvertimeのクラスメソッドで上記の結果を得たいのですが、どのように記述すれば同様の結果が得られるかわからないため、お手数ですがご教示いただきたいです。

参考にした情報

参考サイト1
参考サイト2

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

pry(main)> Overtime.joins(:user).select("overtimes*, users*").group("users.group").group("DATE_FORMAT(date,'%Y年%c月')").sum(:work_time_minute) (3.7ms) SELECT SUM(`overtimes`.`work_time_minute`) AS sum_work_time_minute, users.group AS users_group, DATE_FORMAT(date,'%Y年%c月') AS date_format_date_y_c FROM `overtimes` INNER JOIN `users` ON `users`.`id` = `overtimes`.`user_id` GROUP BY users.group, DATE_FORMAT(date,'%Y年%c月') => {["A", "2019年4月"]=>1828, ["A", "2019年5月"]=>6418, ["A", "2019年6月"]=>6247, ["A", "2019年7月"]=>4820, ["A", "2019年8月"]=>4033, ["A", "2019年9月"]=>6835, ["A", "2019年10月"]=>6717, ["A", "2019年11月"]=>5469, ["A", "2019年12月"]=>4882, ["A", "2020年1月"]=>4365, ["A", "2020年2月"]=>6504, ["A", "2020年3月"]=>5693, ["A", "2020年4月"]=>2593, ["B", "2019年4月"]=>1414,

として解決できました。

投稿2020/04/21 13:14

t.togashi

総合スコア22

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問