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

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

ただいまの
回答率

89.65%

総当り形式のデータ入力欄の作り方

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,361

yoshiky

score 105

お世話になっております。
あるマスタデータの総当り形式でのデータ入力フォームを作ろうとしています。
仮に、「A地点からB地点へ移動すると5pt、B地点からC地点だと10pt」という要件だとします。

|  |A |B |C |  
|--|--|--|--|
|A |  |5 |  |
|B |  |  |10|
|C |  |  |  |

例として、以下の2テーブルがあるとします。

Spotsテーブル
- id
- name

Pointsテーブル
- id
- from_spot_id
- to_spot_id
- point

それぞれの区画には  input type="text"  のテキストフォームがあります。
nameを result_[from_spot_id]_[to_spot_id] として区別する想定です(端折ってます)。
簡単なソース例が下記です。

# controller
@spots = Spot.all

# view
<% @spots.each do |from| %> //縦のループ

    <tr>
        <td><%=from.name%></td>
        <% @spots.each do |to| %>  // 横のループ
            <td>
                <input type="text" name="result_<%=from.id%>?<%=to.id%>" value="">
            </td>
        <% end %>
    </tr>

<% end %>

このようにSpotsテーブルのデータを入れ子でループして、table要素で表を作っています。
POSTされたデータはPointsテーブルへINSERTされます。

前置きが長くなりましたが、ここからが問題で、、
「新規登録時は上記でよいが、更新時はどのデータを取得してviewに展開すればよいか」で悩んでおります。

  • 登録したpointデータはPointsテーブルにあるので、Pointsテーブルからのデータを取得して展開しようすると、Pointsテーブルに存在しない組み合わせのデータが展開できない。
  • 登録時にすべての組み合わせのデータをPointsテーブルにINSERTしておけばレコードは登録できるが、あとからspotsテーブルのデータが追加された場合、やはりPointsテーブルにデータがないのでviewに展開できない。
  • CROSS JOIN と LEFT JOINでがんばってviewに展開しやすい形に加工して取ってくるやり方がありそうだが、CROSS JOIN以外の方法で何とかならないか。

分かりにくい説明で恐縮ですが、何かアドバイスいただければ幸いです。
実ソースでなく、考え方だけでも結構です。

RailsとPHPのタグをつけましたが、言語というよりは考え方の質問なので言語は問いません。
よろしくお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

0

いろいろ考え方がありますが・・・
おそらくご想定されてると思われる、総当たりの表を導出する方法については以下のようにすれば良いと思います。

SpotsテーブルのIDの、直積集合を導出します。
select a.ID as FROM_ID, b.ID as TO_ID from Spots a, Spots b

これに、Points テーブルを外部結合させれば、おそらく目的の総当たり表が導出できます。

select x.FROM_ID, x.TO_ID, y.point
from
(select a.ID as FROM_ID, b.ID as TO_ID from Spots a, Spots b) x
left join Points y
on y.from_spot_id = x.FROM_ID
and y.to_spot_id = x.TO_ID

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/02/03 12:09 編集

    ご回答ありがとうございます。
    アドバイスいただいたSQLで目的のデータが取得できました!!

    やはり直積で取ってくる方法が素直でしょうか。。
    Railsあたりが直積でのデータ取得はあまりしていない雰囲気なので(そういうの場面に出くわしたら、それは設計が悪い?)、処理手順やDB設計の見直しで対応できないかと思って質問させていただいた次第です。

    キャンセル

0

CROSS JOIN(直積)を使わない方法で実装できたので補足します。
redmineのワークフロー設定画面を参考にしました。
https://github.com/redmine/redmine/blob/master/app/views/workflows/_form.html.erb

# controller
@points = Point.all

# view
<% @spots.each do |from| %> //縦のループ

    <tr>
        <td><%=from.name%></td>
        <% @spots.each do |to| %>  // 横のループ
            <td>
                // ここで@pointsにfrom.idとto.idの組み合わせがあるかチェック。
                // あれば変数pointに該当するポイント数をセット。なければ空。
                // コード例)
                //    is_registered = @points.detect{|p| p.from_spot_id == from.id && p.to_spot_id == to.id}
                //    point = is_registerd? p.point : "";
                <input type="text" name="result_<%=from.id%>?<%=to.id%>" value=<%=point%>>
            </td>
        <% end %>
    </tr>

<% end %>

(コード例が若干変ですが)これで何とかやりたいことができました。

SQLで取ってくるほうが全体の処理手順はわかり易いですが、SQL自体が馴染みのないCROSS JOINであることと、spotsマスタが多い場合のパフォーマンスが気になりました。

上記のコードはSQLのコストは少なそうですが、コードがやや複雑なので説明がないと初見では時間がかかりそうと思いました。

アドバイスありがとうございました。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

  • ただいまの回答率 89.65%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる