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

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

ただいまの
回答率

90.34%

  • Ruby on Rails

    7667questions

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

  • SQL

    2548questions

    SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

テーブルの結合について(複数キー)

解決済

回答 3

投稿

  • 評価
  • クリップ 1
  • VIEW 1,508

dare

score 18

前提・実現したいこと

railsを使用しています。

【カテゴリテーブル】

id|name|
1|野菜|
2|果物|
3|炭水化物|

【買い物テーブル】

id|person|c1|c2|c3|
1|たかし|1|2|3|

c1、c2、c3はカテゴリテーブルと関連付けています。

求める結果
1|たかし|野菜|果物|炭水化物|

とカテゴリ名をsqlで算出したいのです。

としたいのですが、どのようなsqlであれば実現できるでしょうか?
railsを利用しているので単純なsqlとrailsでの表現方法も教えていただけるとありがたいです。

よろしくお願いいたします。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+4

多対多のモデルを作るとすっきりするかもです

Person テーブル

id  person 
1 Alice
2 Bob

Category テーブル

id  name 
1 野菜
2 果物
3 炭水化物

PersonCategory テーブル(ひも付きを持つテーブル)

id person_id category_id
1 1 1
2 1 2
3 1 3
4 2 2

↑のようにするとPersonCategoryを経由して
Alice(id: 1) が 野菜(id: 1), 果物(id: 2), 炭水化物(id: 3) 
Bob(id: 2) が 果物(id: 2) 
とひもづいているとわかります

また、上記のテーブルを持つ場合のRailsのモデルの書き方

class Person < ActiveRecord::Base
  has_many :categories, through: :person_categories

  def category_names
    categories.map(&:name)
  end
end
class Category < ActiveRecord::Base
  has_many :persons, through: :person_categories
end
class PersonCategory < ActiveRecord::Base
  belongs_to :category
  belongs_to :person
end

rails consoleで実行すると

alice = Person.find_by(name: 'Alice')
alice.categories
# => [1,2,3]
alice.category_names
# => ['野菜', '果物', '炭水化物']

bob = Person.find_by(name: 'Bob')
bob.categories
# => [2]
bob.category_names
# => ['果物']

参考URL: http://ruby-rails.hatenadiary.com/entry/20141204/1417688260

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/26 19:17

    ありがとうございます。
    レコード数が少ないのでテーブルの作成は避ける形でsqlのみで強引にトライしていたのですが、railsだったらこのテーブルからスマートに取得できるんですね。
    これだと単純で楽ですね。

    キャンセル

  • 2017/01/26 19:29

    データの数が増えるとschemaを変更しないといけないようなテーブルの持ち方は
    現実的では無いですね。
    特に買い物とかだと、たかしくんは何種類のものを買い物をしてもいいとおもうので
    柔軟に対応出来るモデルの設計が良いかと思われます。

    キャンセル

+2

一応正規化を含めた簡単なsampleだけ提示しておきます
railsにそのまま応用できるかどうかはわかりませんが
概ね以下のような考え方をすればよいかとおもいます

  • 元テーブル作成、データ投入
create table tbl_c(cid int not null primary key,name varchar(20));
insert into tbl_c values(1,'野菜'),(2,'果物'),(3,'炭水化物');

create table tbl_k(kid int not null,person varchar(20));
insert into tbl_k values(1,'たかし');

create table tbl_ck(kid int,cid int);
insert into tbl_ck values(1,1),(1,2),(1,3);
  • kidをベースにした購入物名の列記
select t1.kid,person,names
from tbl_k as t1
inner join (
select kid,group_concat(name) as names
from tbl_c as t2
inner join tbl_ck as t3 on t2.cid=t3.cid
group by kid
) as t4 on t1.kid=t4.kid;

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/26 19:09

    何度もありがとうございます。
    かなり複雑なsqlになるんですね。参考にさせていただきます。

    キャンセル

+1

SQLでならこんな感じですかね。同一テーブルを別名を使って複数回 JOIN します。

SELECT t.id, t.person, ta.name, tb.name, tc.name
FROM 買い物テーブル AS t
LEFT OUTER JOIN カテゴリテーブル AS ta ON (ta.id=t.c1)
LEFT OUTER JOIN カテゴリテーブル AS tb ON (tb.id=t.c2)
LEFT OUTER JOIN カテゴリテーブル AS tc ON (tc.id=t.c3)

そもそも買い物テーブルの構造がどうかとは思いますが。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/23 14:06

    買い物数が増えると大変なことになりますね。汗

    キャンセル

  • 2017/01/23 14:27

    元のデータのもたせ方が悪いのでカラムが増えればjoinが増えるのはどうしようもないでしょう。

    そもそもc1とc2、c3の間に相関関係やなんらかの優先順位があるのでしょうか?
    c1~3までは同じ内容を単に3つ並べているだけであれば正規化して
    idに対するカテゴリは列記した別データで持たせるほうが効率的です

    キャンセル

  • 2017/01/23 16:33

    ありがとうございます。
    歪な構成ですが、レコード数は1000以下で少ないので力技でトライしています。
    確かに別にテーブルを用意したほうがスマートですね。

    キャンセル

  • 2017/01/23 18:44

    > dare_subさん
    別にテーブルを持つというよりかは、
    テーブル構造にテコ入れすれば解決する課題だったりしますよ。

    列に横持ちさせる、でなく行持ちさせるに発想を切り替えれば良いです。
    その場合はカラムを追加しての複合主キーを張る・・・という所謂連関エンティティとする手法となりますが。
    (※買い物をする人の情報をマスタ化すると、もはやテンプレですね)

    キャンセル

  • 2017/01/23 22:53

    ありがとうございます。
    ちょっと今の私のレベルでは理解しきれないですね。勉強したいと思います。

    キャンセル

  • 2017/01/24 23:10

    dare_subさん
    お勉強の範囲なら良いですが、実務でテーブル設計していたら大変なことになります。
    とりあえず、正規化を勉強されると良いでしょう。当面、第1正規化から第3正規化まで身に着けてください。
    http://www.atmarkit.co.jp/ait/articles/1109/07/news124.html

    キャンセル

  • 2017/01/26 19:06

    すいません。実務で利用しています(´□`;)
    身内だけの管理画面でレコード数自体が少ないので力技でごまかしている部分があります。
    正規化、勉強します。

    キャンセル

同じタグがついた質問を見る

  • Ruby on Rails

    7667questions

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

  • SQL

    2548questions

    SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。