優先度キューテーブルをクエリするSQLの作業
-
19-08-2019 - |
質問
最初に実行するプロセスを処理する小さなキューを実装しています。これを行うためにデータベースのテーブルを使用しています。以下はテーブルの構造です(SQLiteでモックアップしています):
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,
"identifier" VARCHAR NOT NULL ,
"priority_number" INTEGER DEFAULT 15,
"timestamp" DATETIME DEFAULT CURRENT_TIMESTAMP,
"description" VARCHAR
SQLを記述して、次に実行できるプロセスの行を取得しようとしています。サンプルデータを次に示します。
id identifier priority_number timestamp description
1 test1 15 2009-01-20 17:14:49 NULL
2 test2 15 2009-01-20 17:14:56 NULL
3 test3 10 2009-01-20 17:15:03 NULL
4 test4 15 2009-01-20 17:15:08 NULL
5 test5 15 2009-01-20 17:32:23 NULL
6 test6 14 2009-01-20 17:32:30 NULL
7 test7 7 2009-01-20 17:32:38 NULL
8 test8 20 2009-01-20 17:32:57 NULL
9 test9 7 2009-01-21 13:47:30 NULL
10 test10 15 2009-01-21 13:50:52 NULL
このSQLを使用すると、データを適切な順序で取得できます。
select * from queue_manager order by priority_number, timestamp;
これにより、一番上の優先度番号が最も低い(最も重要な)アイテムが表示され、それらの優先度番号では、一番上の(タイムスタンプで)キューへの最も早いアイテムが表示されます。
このクエリを実行し、最初の行のみを取得できましたが、キューの一番上にあるプロセスの1行を取得するSQLクエリでこれを実行したいです(上記のデータ例では) 、id = 7の行)。
セルフジョインとサブクエリを実行しようとしましたが、メンタルブロックを持っている必要があります-うまくいかないようです。
事前に感謝します!
編集
データベースに依存しないクエリを探していることを忘れていました。これをSQliteでモックアップしていますが、これをDB2またはOracleで実装する可能性は十分にあります。 <!> quot; limit 1 <!> quot;を使用することを考えていました。クエリで型演算子を使用しますが、データベースエンジンによって異なります。
解決
これが機能するかどうかを確認します:
select * from queue_manager where priority_number =
(select min(priority_number) from queue_manager) and
timestamp = (select min(timestamp)
from queue_manager qm2
where qm2.priority_number = queue_manager.priority_number)
他のヒント
select * from queue_manager order by priority_number, timestamp LIMIT 1;
<!> quot; database independency <!> quot;と呼ばれるものに関しては、ほとんどの現実世界のタスクの神話です。原則として、データベースに依存しない方法でスキーマを作成することもできません。
InnoDBのようなもので「並行安全」にしたい場合:
1)「in_progress」フィールドを追加します。
2)AUTOCommitをオフにする
3)SELECT * FROM queue_managerここで、in_progress = 0、priority_number、timestamp LIMIT 1 for UDPATEの順に並んでいます。
4)UPDATE queue_manager SET in_progress = 1 where id = X;
5)コミット
6)仕事をする。その後、行が満足のいくものになったら行を削除します。 「マスタープロセス」に古い「in_progress」ジョブを処理/再デリゲート/クリーンアップさせます。
これを行う最良の方法は、データベースに依存しています。カーソルやその他の構造のすべてのオーバーヘッドに対して、異なるターゲットDBMSに対して異なる取得プロシージャを使用する方がはるかに簡単です。
限られた数の行の選択は、SQLのフレーバーごとに異なる方法で実行されるため、使用しているものによっては、その方法が組み込まれている場合があります。たとえば、MS SQL Serverの場合:
SELECT TOP 1
identifier,
priority_number,
timestamp,
description
FROM
dbo.Queue_Manager
ORDER BY
priority_number,
timestamp
ANSI互換のSQLでこれを行うには、次のメソッドが機能する必要があります。
SELECT
QM1.identifier,
QM1.priority_number,
QM1.timestamp,
QM1.description
FROM
Queue_Manager QM1
LEFT OUTER JOIN Queue_Manager QM2 ON
QM2.priority_number < QM1.priority_number OR
(QM2.priority_number = QM1.priority_number AND QM2.timestamp < QM1.timestamp)
/* If you're concerned that there might be an exact match by priority_number
and timestamp then you might want to add a bit more to the join */
WHERE
QM2.identifier IS NULL
または試すことができます:
SELECT
QM1.identifier,
QM1.priority_number,
QM1.timestamp,
QM1.description
FROM
Queue_Manager QM1
INNER JOIN
(
SELECT
priority_number
MIN(timestamp) AS timestamp,
FROM
Queue_Manager
WHERE
priority_number =
(
SELECT
MIN(priority_number)
FROM
Queue_Manager
)
GROUP BY
priority_number
) SQ1 ON
SQ1.priority_number = QM1.priority_number AND
SQ1.timestamp = QM1.timestamp
どちらの方法も、priority_numberとtimestampの両方で完全に一致することを考慮していないため、それが可能だと思われる場合は(そうでない場合でも)、識別子を使用して1行または2行追加して、一意性を保証する他の何か。または、フロントエンドを記述して、2行を戻すことがある場合に対処します(2番目の行を無視することもできます-次回はそれを取得します)。
各メソッドをテストして、どちらがより適切かを確認します。
また、キューはどれくらいの大きさを期待していますか? ORDER BYでクエリを実行し、フロントエンドに最初の行のみを取得させることは合理的です。
このセクションを読み、最も価値のあるバリアントを選択します適切な互換性。おそらくカーソルの使用は、多かれ少なかれ普遍的に互換性のある唯一の方法ですが、それだけでは価値がないかもしれないパフォーマンスの低下があります(プロファイル!)。
リレーショナルデータベースはキューの管理に向いていません。
Windowsの世界でMSMQ、javaの世界でActiveMQ、またはビジネスの世界でWebsphere MQを見てみてください。
これらの製品は、キューを管理する単一のことを行いますが、それはうまくいきます。