RoR｜sumの計算がおかしくなるケース＃１

# はじめに

何でもかんでも先読みすればすればいいってもんじゃないです。
Bulletを導入してダイアログが出るたびに、
　「はいはい、includesかjoinsすればいいんでしょという」
といった対応だとつらくなるケースがあったのでメモ書きです:smile:

# 前準備

## モデル

モデルはCountryモデル、Prefectureモデル、Cityモデルの３つあります。
リレーションは下記のプログラムを参照してください:smile: 

```ruby:app/models/country.rb
class Country < ApplicationRecord
  has_many :prefectures
end
```

```ruby:app/models/prefecture.rb
class Prefecture < ApplicationRecord
  has_many :cities
  belongs_to :country
end
```

```ruby:app/models/city.rb
class City < ApplicationRecord
  belongs_to :prefecture
  has_one :country, through: :prefecture
end
```

## 検証用データ

```ruby:db/seeds.rb
# 実データに基づくものではありませんのでご注意
# あくまでイメージなのでご了承ください
jpn = Country.create(name: 'Japan')

hyogo = jpn.prefectures.create!(name: 'Hyogo')
osaka = jpn.prefectures.create!(name: 'Osaka')
shimane = jpn.prefectures.create!(name: 'Shimane')
nagasaki = jpn.prefectures.create!(name: 'Nagasaki')

hyogo.cities.create!(name: 'Kobe', population: 22)
hyogo.cities.create!(name: 'Akashi', population: 11)

osaka.cities.create!(name: 'Ikeda', population: 3)
osaka.cities.create!(name: 'Takatsuki', population: 1)
osaka.cities.create!(name: 'Kadoma', population: 7)

shimane.cities.create!(name: 'Matsue', population: 9)
shimane.cities.create!(name: 'Izumo', population: 15)

__END__
各県の合計値は
- Hyogo: 33
- Osaka: 11
- Shimane: 24
- Nagasaki: 未登録
```

# フェッチ

## つらくない場合

Cityモデルから順に結合している場合は問題ありません。
結果として、`{1=>33, 2=>11, 3=>24}`を得ることが出来ています。

＊Prefectureモデルは１から順にHyogo、Osaka、Shimaneが登録されています。

```ruby
City.includes(prefecture: :country).
     group(:prefecture_id).
     sum(:population)
```

```sql
SELECT SUM("cities"."population") AS sum_population,
       "cities"."prefecture_id" AS cities_prefecture_id
FROM "cities"
LEFT OUTER JOIN "prefectures" ON "prefectures"."id" = "cities"."prefecture_id"
LEFT OUTER JOIN "countries" ON "countries"."id" = "prefectures"."country_id"
GROUP BY "cities"."prefecture_id"
```

## つらい場合

つらくなる場合はCityモデルからCountryモデルを参照して、そこからPrefectureモデルを参照したときですね。
「なんでそんなことするん？」というツッコミはなしだぞ:wink:

このときの結果は`{1=>132, 2=>44, 3=>96}`になり先程の結果とかなり違います:sweat_smile:

```ruby
City.includes({prefecture: :country}, {country: :prefectures}).
     group(:prefecture_id).
     sum(:population)
```

```sql
SELECT SUM("cities"."population") AS sum_population,
       "cities"."prefecture_id" AS cities_prefecture_id
FROM "cities"
LEFT OUTER JOIN "prefectures" ON "prefectures"."id" = "cities"."prefecture_id"
LEFT OUTER JOIN "countries" ON "countries"."id" = "prefectures"."country_id"
LEFT OUTER JOIN "prefectures" "prefectures_cities_join" ON "prefectures_cities_join"."id" = "cities"."prefecture_id" 
LEFT OUTER JOIN "countries" "countries_cities" ON "countries_cities"."id" = "prefectures_cities_join"."country_id"
LEFT OUTER JOIN "prefectures" "prefectures_countries" ON "prefectures_countries"."country_id" = "countries_cities"."id"
GROUP BY "cities"."prefecture_id"
```

これは計算対象のレコードがテーブル結合したときに重複してるせいですね:sweat_smile:
SQLを分解して実行してみましょう:smile:

### Cityモデル - Prefectureモデルまで結合

Cityモデル - Prefectureモデルまで結合してもまだレコードは重複していませんね。よく見る外部結合の結果です:eyes:

```sql
SELECT *
FROM "cities"
LEFT OUTER JOIN "prefectures" "prefectures_cities_join" ON "prefectures_cities_join"."id" = "cities"."prefecture_id"
```

テーブルイメージ
![スクリーンショット 2019-03-03 22.48.53.png](https://qiita-image-store.s3.amazonaws.com/0/137044/1d9f16c3-7624-66b0-3b06-2f97586e1558.png)

### Prefectureモデル - Countryモデルまで結合

先程の結果にさらに国（Countryモデル）のレコードを結合していますね。
ここでもまだレコードが重複していませんね:eyes:

```sql
SELECT *
FROM "cities"
LEFT OUTER JOIN "prefectures" "prefectures_cities_join" ON "prefectures_cities_join"."id" = "cities"."prefecture_id"
LEFT OUTER JOIN "countries" "countries_cities" ON "countries_cities"."id" = "prefectures_cities_join"."country_id"
```

テーブルイメージ
![スクリーンショット 2019-03-03 22.49.34.png](https://qiita-image-store.s3.amazonaws.com/0/137044/6491ab33-6f4e-3f98-8e6e-4686995e367d.png)

### Countryモデル - Prefectureモデルまで結合

ここからが問題です:sweat:

先の手順で結合した国（Countryモデル）のレコードから県（Prefectureモデル）を結合しています。
県（Prefectureモデル）は、いま４つありますので、いままでの結果が４つずつ増えることになります。
先程の結果をよく見ると、正しい結果に４をかけたものとおんなじですね:smile:

- 正しい結果  
`{1=>33, 2=>11, 3=>24}`
- 誤った結果  
`{1=>(33 * 4), 2=>(11 * 4), 3=>(24 * 4)}`

```sql
SELECT *
FROM "cities"
LEFT OUTER JOIN "prefectures" "prefectures_cities_join" ON "prefectures_cities_join"."id" = "cities"."prefecture_id"
LEFT OUTER JOIN "countries" "countries_cities" ON "countries_cities"."id" = "prefectures_cities_join"."country_id"
LEFT OUTER JOIN "prefectures" "prefectures_countries" ON "prefectures_countries"."country_id" = "countries_cities"."id"
```

テーブルイメージ（長いので途中は略）
![スクリーンショット 2019-03-03 22.51.24.png](https://qiita-image-store.s3.amazonaws.com/0/137044/2b455597-c0a0-aa3f-6aa7-2e96292f29e9.png)


# まとめ

今回は和を求める場合でしたが、他の場合でもこういった結合をすると問題が出ると思います。
いちどになんでも先読みして済ませようとするとトラブルの元ですし、テーブルも大きくなるのであまりメリットはないかなと思います:wink: