PHPでの質問です。


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

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

回答の条件
  • 1人5回まで
  • 登録:
  • 終了:2012/08/31 10:30:03
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答4件)

id:Sampo No.1

回答回数556ベストアンサー獲得回数104

ポイント25pt

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

他2件のコメントを見る
id:quocard

回答ありがとうございます。

DBはMySQLを使用してます。
なにか自分が思いつかないだけでもっとスマートな方法があるのか?
とも思っていたのですがなかなかそうもいかないようですね。

2012/08/24 13:11:53
id: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は間が空いていないことが前提です。

2012/08/28 11:18:39
id:grankoyama No.2

回答回数560ベストアンサー獲得回数170

ポイント25pt

単純に、

0test1
1test2
2test3
3test4

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


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


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

id:quocard

回答ありがとうございます。
データベース上はpositionの値を変更するだけということになりますので
1 test1
2 test2
3 test3
4 test4

とした場合test3を1個上に上げるとなると


1 test1
3 test2
2 test3
4 test4

とデータベース上はなりますね。

test3を移動させるさいは隣接するpositionの値を取得して↑か↓かによって入れ替える値を選定し、それぞれの入れ替え対象+移動するtestの値をそれぞれ書き換えるという感じですね。

単純に+-するだけはこういった実装はやはり難しそうですね。

2012/08/24 11:36:47
id:windofjuly No.3

回答回数2625ベストアンサー獲得回数1149

ポイント25pt

方法は大きく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. 新しいデータを書き加える
位置変更も新しいデータ追加も、
途中でトラブル停止になると順位がおかしくなるので、
最後まで正しく修正が終わらなかった場合を想定して、
変更前にそれぞれの値を別の場所に保持するようにして、
元に戻すこと(ロールバック)ができるように、
プログラミングしておくべきでしょう。

id:windofjuly

回答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を修正

2012/08/24 11:41:44
id:quocard

回答ありがとうございます。

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

2012/08/29 00:18:11
id:TransFreeBSD No.4

回答回数668ベストアンサー獲得回数268

ポイント25pt

順位を表す数値を、初期値としては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/

id:quocard

回答ありがとうございます。

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

2012/08/29 00:19:26
  • id:grankoyama
    グラ娘。 2012/08/24 11:11:53
    とした場合、test3を一つ↑とした場合に表示を
    test3
    test1
    test2
    test4


    test1
    test3
    test2
    test4
    ではなしに、ですか?
  • id:tdoi
    仕様が曖昧なので、確認させてください。
    リストの要素は全順序を想定していますか?
    もしそうであるならば、質問文にあるようなpositionが全て0という状況を回避するようにするというのが、まず、行うことではないかと思います。
    また、質問文にあるように、ある時点において、半順序であることも認めるのであれば、それなりの準備をする必要があるのかなと思います。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません