人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

PHPでの質問です。

並べ替えを行うにあたっての値の保持について悩んでおります。
リストでよくある↑を押すと複数あるリストの中で1つ上に上がり、↓を押すとリスト内で1つ下がるというものです。
テストのデータベースとして
-------------------
| position | text |
-------------------
|0 | test1|
-------------------
|0 | test2|
-------------------
|0 | test3|
-------------------
|0 | test4|
-------------------
とした場合、test3を一つ↑とした場合に表示を
test3
test1
test2
test4
としたいと思います。
positionの値を+1すれば実現可能ですが、それだとこの時点でtest2を↓にした場合はマイナスにする必要が出てくると思います。
また各項目で↑↓をした場合や、test5と言った値を追加した場合にマイナスの値がある場合などの考慮が必要になってくると思います。

こういう管理手法自体に問題があるような気がするのですが、並べ替えを行うにあたって位置情報をどのように扱えば効率的なのかアドバイスいただけないでしょうか。

●質問者: quocard
●カテゴリ:ウェブ制作
○ 状態 :終了
└ 回答数 : 4/4件

▽最新の回答へ

1 ● Sampo
●25ポイント

直接の回答はできず申し訳ないのですが、というのは、まず仕様が混乱しているうちから実装方法の議論を始めても仕方がないからです。
↑で順位がひとつ上がると考えたのに、実例では↑で最上位まで上がってしまっています。ひとつ上がるのと最上位に上がるの、どちらが正しい仕様ですか?
また、新エントリ追加の際にどういう順位データを与えるべきか、それを考える前にまず新エントリは表中のどこに出現してほしいのか、そこを決める必要があります。


quocardさんのコメント
申し訳ありません。 ↑を押したら値が+1となるのでpositionでソートすると単純にtest3が一番上に来るかな?という程度のものでした。 内容としては以下の内容を想定しております。 test1 test2 test3 test4 の状態でtest3を1回↑をしたら test1 test3 test2 test4 となると想定します。 またtest5を追加した場合は並びとしては test1 test3 test2 test4 test5 として基本的に一番最下にくるように想定してます。 詳細が足らず申し訳ないです。

Sampoさんのコメント
なるほど。 さて、No.3の方も回答しておられるように実装方針は複数考えられますが、どのようにやっても単一レコードの書き換えでは無理です。 DBによってやり方がいろいろと変わってくるのですが、DBは何をお使いですか? mysql?

quocardさんのコメント
回答ありがとうございます。 DBはMySQLを使用してます。 なにか自分が思いつかないだけでもっとスマートな方法があるのか? とも思っていたのですがなかなかそうもいかないようですね。

Sampoさんのコメント
MySQLですね… MS SQL Serverユーザーなので動くか検証できないのですが、MySQLは複数テーブルUPDATEが書けるので update table as a, table as b set a.position = a.position -1, b.position = b.position +1 where a.position = b.position + 1 and a.position=指示された行の順位 こんな感じでできるでしょうか。 positionは間が空いていないことが前提です。

2 ● グラ娘。
●25ポイント

単純に、

0test1
1test2
2test3
3test4

としておいて、↑や↓の操作で、上下のポジション値を入れ替えればいいんじゃないですか?


それだと、追加したものは最大値+1にしておけばいいですし、
二つのデータの入れ替えだけなので、手間もそうかかりません。


仮に、test4を一気に一番上にするとか、test1とtest2の間に(1ことばし)とかいう
操作が必要なのなら、別途検討だとはおもいますが。


quocardさんのコメント
回答ありがとうございます。 データベース上はpositionの値を変更するだけということになりますので 1 test1 2 test2 3 test3 4 test4 とした場合test3を1個上に上げるとなると 1 test1 3 test2 2 test3 4 test4 とデータベース上はなりますね。 test3を移動させるさいは隣接するpositionの値を取得して↑か↓かによって入れ替える値を選定し、それぞれの入れ替え対象+移動するtestの値をそれぞれ書き換えるという感じですね。 単純に+-するだけはこういった実装はやはり難しそうですね。

3 ● うぃんど
●25ポイント

方法は大きく2つくらいでしょうか・・・

(1)データ件数が非常に少ない場合

関係するデータの position の値を全て書き換える

(2)データ件数が多い場合

position ではなく 通し番号No、前preNo、次nextNoの情報を用いる。

下記では、最上位に来るレコードは preNo = 0 として、
最下位に来るレコードは nextNo = Null(未定義) とすることで、
それぞれ基点と終点を表すこととしています。

NotextpreNonextNo
1 test102
2 test213
3 test324
4 test43Null

No.3(test3)を最上位に移動させる場合のデータ修正は次のようになります。
1. preNoとnextNoがNo.3になっているレコードのpreNoとnextNoを修正
2. preNoがゼロ(すなわち最上位)のpreNoを修正
3. No.3のpreNoとnextNoを修正
データが何件になろうとも、修正するのは常に4レコード分で済むため、
データが多ければ多いほど(1)との処理速度で比較にならないほどの差が出てきます。
新しいデータtest5を追加する手順は次のようになります。
1. nextNoがNullのレコードを探す
2. 新しいデータNoを 5 に決定する
3. Null を 5 に書き換える
4. 新しいデータを書き加える
位置変更も新しいデータ追加も、
途中でトラブル停止になると順位がおかしくなるので、
最後まで正しく修正が終わらなかった場合を想定して、
変更前にそれぞれの値を別の場所に保持するようにして、
元に戻すこと(ロールバック)ができるように、
プログラミングしておくべきでしょう。


うぃんどさんのコメント
回答No.1への 2012/08/24 11:09:38 時点の返信を見て追記 No.3(test3)を1つ上に移動させる場合のデータ修正は次のようになります。 1. nextNoがNo.3になっているレコードのnextNoを修正 2. No.3のpreNoとnextNoを修正 No.3(test3)を1つ下に移動させる場合のデータ修正は次のようになります。 1. preNoがNo.3になっているレコードのpreNoを修正 2. No.3のpreNoとnextNoを修正

quocardさんのコメント
回答ありがとうございます。 お返事がおくれて申し訳ありません。 やはりなかなか難しいですね。1つの値の操作ではなく2つの値を操作するほうがソース的にも効率がよさそうです。 参考にさせていただきます。

4 ● TransFreeBSD
●25ポイント

順位を表す数値を、初期値としては100とか1000ずつにして、変更するときは挿入する上下の間の値に変えるという方法もあるかと思います。
例:
初期値

1000test1
2000test2
3000test3
4000test4

test3を1つ上の場合、前二つのtest1とtest2の値を取得して、test3をその間(平均値)の1500にする。

1000test1
1500test3
2000test2
4000test4

この状態でtest2を一つ上なら1250、一つ下ならtest4の下はないので4000+1000、test3をもう一度上ならtest1の上がないので0との間をとって500 とか、新規追加はmax+1000とか。

利点はその時点での更新レコードが1つで済むこと。おそらく並べ替えの実行より圧倒的に多いであろう、並び順のリスト取得が行いやすいことなどがあります。
欠点は何度も並べ替えると間が詰まってくるので、番号を振りなおす処理を適宜(あまり頻繁ではコードが複雑な割に毎回振りなおすのと変わらなくなるし、あまり遅いと、その間に入れる間隔がなくなってしまう可能性がある)行う必要があるとか、間がない場合の例外処理が必要だとか、コードの流れが単純でなくなることでしょうか。

番号振り直しのタイミングは、例えば並べ替え時に間隔が100以下になったら、例えばdirtyフラグ持たせるとかキューに入れるとかして、cronで回して……とかありますが、あまり凝った事をするとバグの要因になりますし、他の方法も含め、リストの数や他の部分の規模との兼ね合いかなと。

[追記]
データベース上での逐次並べ替えという流れでしたが、javascript使ったUIも視野に入れ、ブラウザ上で並べ替えを行い、決定した段階でサーバに送信、データベースを更新という流れならば、サーバサイドでは+-など演算を考える必要なく、送られてきたリストに従い順番に順位を更新するだけでよくなります。
http://alphasis.info/2011/05/jquery-ui-sortable-submit/


quocardさんのコメント
回答ありがとうございます。 お返事が遅れて申し訳ありません。 これだと単一の操作でいけそうですが回数が重なってきた場合の例外処理が面倒そうですね。 とはいえ簡単なものならこういった手法でもいけそうな気がします。 参考にさせていただきます。
関連質問

●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ