2017-08-02 12 views
1

私は、行がpositionフィールドを持つMariaDB(MySQLと思う)データベースを持っています。したがって、たとえば、ユーザーがID 10の列が位置#1に移動することもできますデータベースの行順序フィールドをより効率的に管理する方法?

CREATE TABLE `ordered_data` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `owner` int(11) NOT NULL, 
    `data` varchar(300) COLLATE utf8_unicode_ci NOT NULL, 
    `position` int(11) NOT NULL 
    PRIMARY KEY (`id`) 
) 

:この位置は変更できますが、常に連続している必要があり、そして1

簡素化されたテーブルスキーマで開始します。これにはもちろん、後続のすべての項目の並べ替えが必要です。それ以外の場合は、positionフィールドに重複があります。私は4つ以下のクエリでこれを行う方法を理解できません。

私の現在の解決策は以下の通りですが、それは非常に簡単ですが、はるかにエレガントな再注文​​の方法があると感じています。この例では、私は3

  1. を位置決めするために、ユーザ20により、ID32を持つ行を移動していSELECT position FROM ordered_data WHERE id = 32によってcurrentPositionを取得します。
  2. 私たちが作成しようとしているギャップをUPDATE ordered_data SET position = position - 1 WHERE position > currentPosition AND owner = 20で埋める。
  3. 行の新しい位置(3)のスペースをUPDATE ordered_data SET position = position + 1 WHERE position >= 3 AND owner = 20とします。
  4. ギャップに収まるように行の位置を更新しますUPDATE ordered_data SET position = 3 WHERE id = 32

すべての提案は大歓迎です。最初の2つのステートメントを副選択と組み合わせると、MySQLは更新と選択に同じテーブルを使用することについて不平を言っていました。

オーナーあたり10行を超える可能性は低いです。

+1

グループあたり10行を使用しても、パフォーマンスは気にせず、最も簡単な(最も読みやすい)ソリューションを使用します。あなたはすでにそれを持っています。私はちょうど1,3,4,2に注文を変更します - これはまた、 '(所有者、位置)'のユニークなキーで動作します。 –

+0

@PaulSpiegelリオーダーがうまくいかない(ストレートスワップが隙間を残してしまい、ポジションの周りに頭を浮かべるのは難しいですが)、DBレベルで強制するのが良いアイデアです。 – JakeSteam

答えて

1

手順の順序を変更することは、思ったほど単純ではありません。しかし、もう一つのステップでは、(owner, position)のユニークなキーでアルゴリズムを動作させることができます。手順2で重複エントリエラーを回避するには、移動する項目にpossition = 0を一時的に割り当てることができます。完全なアルゴリズムは、次のようになります。

この方法は、小グループでは(IMHO)罰金です。より大きなデータセットの場合は、ステップ3.と4.を1ステップで最適化して実行することができます。次の例を見てください:100万のアイテムのグループがあり、アイテムをポジション7からポジション3に移動したいとします。アイテムをそのポジションから「削除」したら、ポジション8を1000000に更新し、それらを1ずつ減らしますギャップを閉じる。次に、位置3を999999に更新し、スペースを増やすためにそれらを増やします。これは擬似コードです:私たちが必要とするすべては、これを実現するために6に3位をインクリメントしている間、これは、二回のほぼ全体のグループを更新してしまう、2と3は

if (@new_pos < @old_pos) 
    update ordered_data 
    set position = position + 1 
    where position owner = @owner 
     and position between @new_pos and @old_pos 
    order by position desc 
else if (@new_pos > @old_pos) 
    update ordered_data 
    set position = position - 1 
    where position owner = @owner 
     and position between @old_pos and @new_pos 
    order by position asc 
else 
    -- do nothing 

注意と交換することができる手順。アプリケーションサイトで正しいクエリを選択する必要があります。

あなたも、単一のクエリにそれらを組み合わせることができます。

update ordered_data 
    set position = position + sign(@new_pos - @old_pos) 
    where position owner = @owner 
     and position between @new_pos and @old_pos 
    order by position * sign(@new_pos - @old_pos) desc 

しかし、この場合にはエンジンはおそらく、GROUP BY句のインデックスを使用することはできません。

+0

ちょっと、これはPDO(PHP MySQL)を介して行われます。つまり、複雑なクエリは実装するのが簡単ではありません(もちろんストアドプロシージャなどで可能です)。 (IDを一時的に0に設定する)最初のアプローチは有効ですが、複数の並べ替えが発生した場合、固有の競合が発生する可能性はごくわずかです。 Tyvmは2番目の部分については、データが予想以上に大きくなった場合に備えて保持しています。 Vが役に立ちました! – JakeSteam

関連する問題