0

コンテキスト:次の
ようなテーブルを持つSQLiteデータベースがあります:
CREATE TABLE items (item TEXT, position INTEGER)

このテーブルには、数百万のエントリが含まれています。列positionに索引が付けられます。

私のフロントエンドは、列に適用する一連の更新を作成することがありpositionます。更新対象の行は、ID によって識別されません。これは、更新が広範囲の項目に対して行われる可能性があり、関連するすべての ID をリストすると非常にコストがかかる可能性があるためです。たとえば、「位置 >= 500 & 位置 <= 10000 に +10 を追加する」-「位置 >= 100000 & 位置 <= 100003 に -3 を追加する」などの更新アクションをまとめて実行できます。

問題: 更新の束の更新の条件付き位置はすべて、束ねられた更新の前の位置の値に基づいています。一括更新の各更新アクションを順次実行すると、更新の 1 つの後に「重複」の問題が発生する可能性があります (実際には存在します)。

例によって:

item | position
it1  | 1
it2  | 2
it3  | 3
it4  | 4
it5  | 5
it6  | 6
it7  | 7

この一連の更新がある場合:「位置 >= 5 & 位置 <= 6 に + 2 を追加」-「位置 >= 3 & 位置 <= 4 に - 2 を追加」これを次の SQLite クエリに変換します。
UPDATE items SET position=position-2 WHERE position >= 5 AND position <= 6
UPDATE items SET position=position+2 WHERE position >= 3 AND position <= 4

私はこの結果を得るつもりです:

 it1 | 1
 it2 | 2
 it3 | 5
 it4 | 6
 it5 | 5
 it6 | 6
 it7 | 7

私が望むものの代わりに、それは言うことです:

 it1 | 1          {  it1 | 1  }
 it2 | 2          {  it2 | 2  }
 it3 | 5          {  it5 | 3  }
 it4 | 6    ===>  {  it6 | 4  }
 it5 | 3          {  it3 | 5  }
 it6 | 4          {  it4 | 6  }
 it7 | 7          {  it7 | 7  }

これは、ある操作から別の操作への「重複」によるものです。

私の最初のアイデアは、次のように CASE を使用することでした:

UPDATE items SET position=CASE WHEN position >= 5 AND position <= 6 THEN position-2 WHEN position >= 3 AND position <= 4 THEN position+2 ELSE position END
このソリューションは正常に機能しますが、主要な関係者が更新に関与していなくても、SQLite がテーブルの何百万ものエントリに対してこれを実行しているように見えるため、非常に遅いです。

だから私はこれを次のように変更しました:
UPDATE items SET position=CASE WHEN position >= 5 AND position <= 6 THEN position-2 WHEN position >= 3 AND position <= 4 THEN position+2 ELSE position END WHERE (position >= 5 AND position <= 6) OR (position >= 3 AND position <= 4)
このソリューションは問題なく動作し、SQLite は関連する行でのみ更新を実行するため、非常に高速です。

質問 : 1 回の更新で 10 回、または 100 回の更新アクションを行うことができるため、クエリが非常に大きくなる可能性があり、これはあまり見栄えがよくありません...「美しい」/「素敵」。

これを処理するためのより美しい方法があると思いますか? SQLite は、影響を受ける行を最初に選択することでいくつかの「UPDATE」クエリを実行し、その後行を効果的に更新できますか?

アイデア?考え ?

どうもありがとうございました !

4

2 に答える 2

0

SOへようこそ。

別の解決策があると感じていますが、あなたの質問に従って、スキーマを変更します。

CREATE TABLE items (item TEXT, position INTEGER, position_tmp INTEGER)

次のように更新を実行します。

UPDATE items SET position_tmp = position + 1 WHERE 100 <= position AND position < 200
UPDATE items SET position_tmp = position - 3 WHERE 500 <= position AND position < 1000
...

ついに、

UPDATE items SET position = position_tmp

余分な列を永続的に持つのは不快に思えるかもしれposition_tmpませんが (実際にそうです)、SQLite は列の名前変更や削除をサポートしていません。

編集:

なぜあなたがこれをやろうとしているのか、今なら理解できると思います。注文を課したい何百万ものアイテムがあります。リストの途中から 1 つを削除した場合は、新しい順序を反映するように残りを調整する必要があります。

必要なものによっては、浮動小数点数を使用して順序付けできる場合があります。たとえば、0.1、2.5、5.0 です。2 番目の要素として何かを挿入する場合は、位置 1.3 で挿入します。最後の要素を削除したい場合は、それを削除してください。

それ以外の場合は、基本的に、リストの挿入と削除が高速であるという古くからの問題に対処しています。

于 2013-10-27T05:19:26.037 に答える
0

問題をアルゴリズム的に確認する必要があります。

positionからAまでのアイテムに追加:P1P2

positions in [P1, P2] will become [P1+A, P2+A]
positions in [P2+1, P2+A] will become [P1, P1+A-1] (shift: -(P2-P1+1)=P1-P2-1)

SQL:

UPDATE items SET position=CASE position <= P2 THEN position+A ELSE position-P1-P2-1 WHERE position BETWEEN P1 AND P2+A;

positionからSまでの項目を減算する場合:Q1Q2

positions in [Q1, Q2] will become [Q1-S, Q2-S]
positions in [Q1-S, Q1-1] will become [Q2-S+1, P2] (shift: P2-P1+1)

SQL:

UPDATE items SET position=CASE position >= Q1 THEN position-S ELSE position+P2-P1+1 WHERE position BETWEEN Q1-S AND Q2;

したがって、これら 2 つのクエリのいずれかを使用すると、関連するすべての位置を 1 つのステートメントで更新できます。適切な (P1, P2, A) または (Q1, Q2, S) を使用してください。

于 2013-10-28T10:26:00.493 に答える