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

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

新規登録して質問してみよう
ただいま回答率
85.35%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

データ構造

データ構造とは、データの集まりをコンピュータの中で効果的に扱うために、一定の形式に系統立てて格納する形式を指します。(配列/連想配列/木構造など)

Q&A

5回答

2086閲覧

レコード数の多いMySQLの順番決めに関する構造のベストプラクティス

tesopgmh

総合スコア146

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

データ構造

データ構造とは、データの集まりをコンピュータの中で効果的に扱うために、一定の形式に系統立てて格納する形式を指します。(配列/連想配列/木構造など)

0グッド

0クリップ

投稿2020/07/10 03:44

編集2020/07/10 05:00

お世話になります

レコード数の多いMySQLの順番決めに関する構造のベストプラクティスを
皆様にお聞きしたいです

もしかするとアルゴリズムの質問になるかもしれません

例えば以下のように、順番を管理するカラムorderを作って
任意に出力順を決めるような構造にしたとします

idnameorder
1いちご1
2メロン4
3りんご2
4バナナ3
SELECT `name` FROM `table` ORDER BY `order` ASC

出力結果は
いちご、りんご、バナナ、メロン

期待通りです
このような「順番用のカラム」を作ってORDERするような構造はよく使われていると思いますが

例えば、10万件ほどのデータがあり
ユーザーが順番を任意にソートして保存できるような機能を採用した場合

例えば、order 50,000位だった列をorder 1位に移動すると5万件のレコードの更新が必要になってしまうと思います

idnameorder(更新前)order(更新後)
50000そば500001
1いちご12
2メロン45
3りんご23
4バナナ34
5...続く

ユーザーが順番を任意にソートして保存できる機能ですので
7万位から2万位に動かす場合、5万位から2位 and 6万位から5位 and 2位から9万位など複数のソートがある場合があります

ユーザーが順番を任意にソートして保存できる機能は
一回限定の機能ではなく繰り返し行われます

これが100万件になったら、1億件になったら、ソートだけでとんでもない負荷になると思います
ですので、レコード数が多い場合「順番用のカラム」を作ってORDERするような構造は良いものとは言えないかなと思います

「順番を任意に保存できるようにしたい」「レコード数が多い」場合
みなさんはどのような構造にしているのでしょうか??

なにかアルゴリズムで対応しているのでしょうか
事例やヒントをいただけますと幸いです

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

順番がやすく変更できるのがリンクリストらしいと思います。orderではなくnextを記録すればどうでしょうか?例えば:

idnameorder(更新前)order(更新後)next(更新前)next(更新後)
50000そば500001500011
1いちご1233
2メロン4555
3りんご2344
4バナナ3422

行を移動する際、その行、その前後行と行先の前後行、まとめて5行だけ更新していいです。

しかし、その代わり、読み込みの際、SQL側でORDERできなくなります。


もう一つ書き方はStackOverflowからパクってきたアイディアなんですが、orderの間を大きくすれば、順番更新の際に間の中に入れば、多数のレコードを更新する必要が後押せます。デフォルトのINTEGERのidは最大値が21億であり、2億のデータでもorderの間に10を留めれば大丈夫でしょう。

idnameorder(更新前)order(更新後)next(更新前)next(更新後)
50000そば5000005500011
1いちご101033
2メロン404055
3りんご202044
4バナナ303022

お役に立てば幸いです。

投稿2020/07/10 05:27

YufanLou

総合スコア464

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tesopgmh

2020/07/10 12:26 編集

ありがとうございます! おおおおお、前半、目から鱗でした 次のカラムのid情報を保持するカラムでしたら 最大5カラムの更新で済みますね しかも更新は何度も繰り返せます! 後半はyambejpさまから頂いたやり方に近いですね 例えば10おきだと11回の挿入ができなくなるので 繰り返しソートされると破綻するかもしれません
YufanLou

2020/07/11 06:35 編集

そうです、でも前半はデータベースがORDERできなくなり、ページ付けもできなくなります。すべての行を取得し、アプリ側でnextに沿って表示したり;試していっぱいの行を取得し、nextに沿って見つけなかったらもっと取得したりしなければなりません。つまり更新しやすいの代わりに読み込みが難しくなるのです。この取捨をしっかり考えてください。いや、考えるより、テストして本当にユーザー体験を向上できるか検証しなさい。 後半はご意見の通りいつか破綻しますけど、行の数とユーザーの行動に合わせて調整してみれば、破綻までの時間をなるべく長くできますか、再整理を段階的に行い破綻のタイムコストを敷けてユーザー体験を維持できます。 調整とは例えば、INT型が負数合わせて42億以上の数字を含めています。2億行なら20おきできます。BIGINTを使えば10億おきもできますが、行ごとINTより4Byte増必要なので400MB増のストレージを使ってしまいます。 再整理を段階的にするとは例えば、おきが定数ではなく、範囲を行数で分けて決めます。そして全部を再整理ではなく、いくつかの条件で再整理の範囲を決めます。更新の際にはおきの真ん中(order_front + order_back / 2)に入れます。条件とは例えば前後3000行とか前後10万順番とかで、テストしながら調整していきなさい。 最終的には、実行時間、ストレージ、コードの難しさ、この三つの間に取捨しなければなりません。テストと検証しながら最善の策を探りましょう。
YufanLou

2020/07/11 07:06

読み込みの難しさは例えば、2億行すべて取得すれば、idとnextだけ取得しても800MBのメモリーが必要です。行のデータを取得すればメモリーもっと必要ですが、取得しなければ後で順番で1行1行取得になってしまいますので、実行時間が長くなりそうです。 一回すべての行を取得しなければ、少ないほどnextを見つけないリスクが高くなって、再取得しなければならなくて実行時間が長くなります。 そもそもスクリーンに2億行が多分すべて表示できないので、用例にとっていいところがコストに見合いかを検証してみてください。
guest

0

order 列を integer でなく float にしては?
7万位から2万位に動かす場合, 2万位 と 1万9999位の中間の値を設定する様にするのです。
order 列には index を張っておくことは必要です。
1万9999位 と 2万位のレコードは order by order offset 19998 limit 2 で得られます。

投稿2020/07/11 01:22

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sazi

2020/07/11 01:39

割り算で誤差が生じないですかね?
katoy

2020/07/11 02:31

誤差は関係ないです。大小関係さえ保たれればよいので。 もし (a + b) /2 が a か b になったなら、 a, 挿入する値, ,b の3つを調整する必要は出てきますが。
guest

0

orderとは別に枝番(sub order)を項目として追加すれば、同じorder内のデータのみの更新で済みます。
また、その枝番も追加なら100番おきにして置き、挿入に備えるようにすると、更新対象はさらに減らすことができます。

結局、並べ替えを想定して、予め隙間をどのように準備しておくかという事

追記

ソートをツリーと見立てて再帰を使用して行うなら、3件だけの更新で済みますが、性能面で現実的では無いと思います。

投稿2020/07/10 04:44

編集2020/07/10 13:42
sazi

総合スコア25327

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tesopgmh

2020/07/10 05:04

ありがとうございます すみません条件が不足しておりました ユーザーが順番を任意にソートして保存できる機能は 一回限定の機能ではなく繰り返し行われます ソートを行うたびに枝番を増やすと 全レコード更新とはまた別のコストがかかりそうです
sazi

2020/07/10 05:30

枝番を増やすとは? 枝番とはカラムの事です。 質問の例なら (order, suborder)=(50000, 0) →(1,-1) のように更新する事です。
tesopgmh

2020/07/10 12:23

私の理解不足でした申し訳ございません この方法でも sub orderに記録してソートした回数分 蓄積して更新回数が増えていくのではと思います sub orderが1~5000まで割り振ったのちにsub order:2500の場所にデータをソートした場合など
sazi

2020/07/10 13:53 編集

suborderに隙間が無い場合に振り直ししたとして、枝番の部分だけですからorderだけよりはましですね。 実際に同じ隙間に何度も並び替えが起こることが想定できるなら、最初は1,000番単位で振るようにすれば、良いでしょう? (回答したように「予め隙間をどれだけ準備できるか」が更新対象を減らす事に繋がります) 因みに、追記した部分は理解されたでしょうか。
guest

0

そもそもこのテーブルでソート用のカラムがないなら
どういうロジックで順位をきめるかわかりませんよね
一般的なランク付け処理は1データごとに処理がはいるので重くなりがちです

投稿2020/07/10 03:50

yambejp

総合スコア116724

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tesopgmh

2020/07/10 04:32

>ソート用のカラムがないなら 「順番用のカラム」はorderなのですが sort用のカラムとは具体的にどういったものでしょうか?? >どういうロジックで順位をきめるか 「順番用のカラム」order を ORDER ASC します
yambejp

2020/07/10 04:45 編集

順番用のカラムは前後のランクとの大小関係しかみなくていいのですね? であれば50000を1の前に持っていきたいなら0にすることです。 intではなくdoubleか何かにしておけば、50000を2と3の間に入れるときは 2.5にすればいいです ランク付けはorderカラム自分より小さい数をcountして1足したものです
tesopgmh

2020/07/10 04:59

ありがとうございます すみません条件が不足しておりました ユーザーが順番を任意にソートして保存できる機能は 一回限定の機能ではなく繰り返し行われます >intではなくdoubleか何かにしておけば、50000を2と3の間に入れるときは >2.5にすればいいです double小数点第一位に設定した場合、2と3の間に11回以上入れると上記は破綻すると思います intを歯抜けにした場合も同じで更新回数に制限がついてしまいます
yambejp

2020/07/10 09:57

中割をするのがやなら二次ソート列をつくるくらいですかね create table tbl(id int primary key,name varchar(10),sort1 int,sort2 int,unique(sort1,sort2)); insert into tbl values (1,'いちご',1,0), (2,'メロン',4,0), (3,'りんご',2,0), (4,'バナナ',3,0), (50000,'そば',50000,0); id=50000をsort1=2の次にいれるには update tbl set sort1=2,sort2=1 where id=50000; select * from tbl order by sort1,sort2
tesopgmh

2020/07/10 12:21

なるほど!! saziさまが同様の回答をくれていたのですが yambejpさまの具体例でやっと理解できました しかしこの方法でも sort1=2の次にソートした回数分 蓄積して更新回数が増えていくのではと思います sort2が1~5000まで割り振ったのちにsort2:2500の場所にデータをソートした場合など
yambejp

2020/07/10 14:22 編集

頻繁でなければ定期的に振り直す処理が必要にはなりそうですね 月イチくらいのメンテでいけるんじゃないですかね?
guest

0

例えば、order 50,000位だった列をorder 1位に移動すると5万件のレコードの更新が必要になってしまうと思います

そばのorderを-1にすれば回避できます。

投稿2020/07/10 03:49

maisumakun

総合スコア146018

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2020/07/10 03:52

> 「順番を任意に保存できるようにしたい」「レコード数が多い」場合 それが必要になる状況のほうが想像できません(1億件のリストを任意にソートするといっても、おそらく人間が対応しきれません)。
tesopgmh

2020/07/10 04:43 編集

すみません、私の出した例が少なかったです 例の5万位から1位の例ではその通りですが ユーザーが順番を任意にソートして保存できる機能ですので 5万位から2万位の場合、5万位から2位 and 6万位から5位 and 2位から9万位など複数のソートがあった場合 その方法では期待する結果にはならないです
maisumakun

2020/07/10 03:55

ソートはどれくらいの頻度で行われるものなのでしょうか?「たまに負荷が発生する」程度であれば気にすることもないでしょう。
maisumakun

2020/07/10 03:57

状況がはっきりすれば、もっといいデータ構造、操作体系があるかもしれませんので、もう少し具体的にしていただけないでしょうか。
tesopgmh

2020/07/10 04:44 編集

ありがとうございます! 質問内容は何かの要件をデフォルメしているわけではなく書いた通りの要件でございます! 10万件のデータを 5万位から2位 and 6万位から5位 and 2位から9万位 など任意に順位変更して その順位を1~10万件まで正確に出したいです 特に特殊な要件ではないかなと思ったんですが これを実現したいなら、全データのorderをUPDATEするしかないのですかね
tesopgmh

2020/07/10 04:42

正確には全データではなく全対象データですね
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問