現在の合計を使用して、加重セットの中央値を見つけています。これは SQL では正常に機能しますが、Hibernate が FROM 句でサブセレクトをサポートしていないため、hql では機能しません。実際のコードには、hql に既に配置されている多くの動的クエリ構築が含まれているため、簡単に SQL にドロップすることはできません。
サンプルテーブルは次のとおりです。
score weight
2 1
5 1
5 1
6 1
7 1
10 2
10 2
合計スコアは 9 です (このクエリの前にこれを知っています)。9/2 = 4.5 であるため、このクエリは加重中央値スコアとして 6 を返す必要があります。
サンプルクエリは次のとおりです。
SET @runtot:=0;
SELECT
q1.score
FROM
(SELECT
score, (@runtot:=@runtot + weight) AS rt
FROM
tmp_stddev
ORDER BY score) as q1
WHERE
q1.rt <= (9 / 2)
ORDER BY q1.score DESC
LIMIT 1;
サブセレクトでスコア ASC によって並べ替えると、中間点に到達するまで重みを追加し続けることができます。外側のクエリで DESC を注文すると、LIMIT を使用して、最高のパフォーマンスを得るために 1 つの結果だけを返すことができます (ここには大量のデータが存在する可能性があるため、実際には 1 つの結果のみを返したいと考えています)。
これは SQL では機能しますが、HQL では機能しません。クエリ内のユーザー変数の設定をサポートすると思われるカスタム方言を作成できます (それを 0 にクリアする部分は、同じ接続に対する別の SQL クエリになります)。問題は副選択です。
私はこれを行うことができます:
SET @runtot:=0;
SET @runtot2:=0;
SELECT
score,
(@runtot := @runtot + weight) AS rt
FROM
tmp_stddev
WHERE (@runtot2 := @runtot2 + weight) <= (9/2)
ORDER BY score;
しかし、これはすべてのスコアを返します。実際には 1 つだけが必要です (データセットは非常に大きくなる可能性があり、速度が重要です)。
これを書き直して単一の結果を返し、高速にし、hql が生成できる sql の形式にする方法について何か提案はありますか?
更新: 以下の Mosty Mostacho の提案とその他の調査に基づいて、これは一貫して機能するようです。
SET @runtot:=0;
SELECT
score, weight, @val := score
FROM
tmp_stddev
WHERE
(@runtot := @runtot + weight) <= (9 / 2)
ORDER BY score;
ここで、最後に一致したスコアを変数に選択することで、後でその値を選択して同じ接続で使用し、並べ替えリストの最後の項目を取得できます。これが私が望むものです。また、ユーザー定義変数の読み取り/書き込みの範囲を縮小しました。これは、データを変更したときに一貫性がないように見えました。
質問:
- これはユーザー定義変数の安全な使用法ですか? 同じステートメントでそれらを読み書きすることがどれほど安全でないかについて多くのことを読んできましたが、読み書きは両方とも HAVING 句の単一の式の一部であるため、これは順番に評価する必要はありませんか? 言い換えれば、これは信頼できますか?
- これを HQL で動作させるにはどうすればよいですか? カスタム方言を使用して、「@val := スコア」部分を実行するカスタム関数を作成すると、「無効なフィルタ パラメータ名形式」という例外が発生します (コロンが原因だと思いますが、それだけではいけません)。 Hibernate による HQL から SQL への直接パススルー置換? コロンがあることを気にするのはなぜですか?)
- 上記のクエリで考慮していないより良い解決策はありますか?