シナリオ:
Phoenixを使用してクエリサービスを構築し、サーバー側にインターフェースを提供する必要がありました。
設計:
Spring BootでRESTful APIを公開し、MyBatisでPhoenixのSQL処理を行うため、Spring Boot + MyBatis + Phoenixの統合方案を採用しました。
しかし、ここで重大な問題が発生しました!
シナリオの詳細:
ネットワークタクシーの予約単クエリ処理を実装する必要がありました。この処理では6種類の時間情報を使用する必要がありました:
- 注文プッシュ時間
- 有効な注文受付時間
- 注文受注成功時間
- 注文キャンセル時間
- 目的地到着時間
- 支払い時間
課題:
日を跨いだ支払いが発生する可能性があるため、基礎テーブルに対して6回のクエリを実行し、それぞれ異なる時間条件で検索し、最後にUNION処理と合計計算を行う必要がありました(ここで問題が発生しました!)
問題のあるコード
<select id="searchContext" resultType="com.df.entity.SearchInfo">
SELECT
TB."driver_id",
TB."driver_name"",
TB."mobile",
TB."alliance_name",
TB."register_city",
TB."driver_type",
SUM(TB."pushOrderNum") AS "pushOrderNum" ,
SUM(TB."effectiveSlogan") AS "effectiveSlogan",
SUM(TB."grab_sus_order") AS "grab_sus_order",
SUM(TB."cancel_order_num") AS "cancel_order_num",
SUM(TB."cancel_passenger_num") AS "cancel_passenger_num",
SUM(TB."cancel_driver_num") AS "cancel_driver_num",
SUM(TB."cancel_service_num") AS "cancel_service_num",
SUM(TB."destination_reached_num") AS "destination_reached_num",
SUM(TB."pay_num") AS "pay_num",
SUM(TB."order_cumulative_num") AS "order_cumulative_num",
SUM(TB."order_pay_cumulative_num") AS "order_pay_cumulative_num"
FROM
(
SELECT
"driver_id" ,
"driver_name" ,
"mobile" ,
"alliance_name" ,
"register_city" ,
"driver_type" ,
sum(to_number("pushOrderNum")) AS "pushOrderNum" ,
sum(0) AS "effectiveSlogan" ,
sum(0) AS "grab_sus_order" ,
sum(0) AS "cancel_order_num",
sum(0) AS "cancel_passenger_num",
sum(0) AS "cancel_driver_num",
sum(0) AS "cancel_service_num" ,
sum(0) AS "destination_reached_num" ,
sum(0) AS "pay_num" ,
sum(0) AS "order_cumulative_num",
sum(0) AS "order_pay_cumulative_num"
FROM
"_crontab_reservation"
WHERE
CASE WHEN "pushOrderTime" != '0' THEN
TO_DATE("pushOrderTime",'yyyy-MM-dd') >= TO_DATE(#{start_time},'yyyy-MM-dd')
AND
TO_DATE("pushOrderTime",'yyyy-MM-dd') <= TO_DATE(#{end_time},'yyyy-MM-dd')
ELSE 1=1 END
<if test="driver_id != null and driver_id !=''" >
and "driver_id"= #{driver_id}
</if>
<if test="driver_name != null and driver_name !=''" >
and "driver_name" = #{driver_name}
</if>
<if test="mobile != null and mobile !=''" >
and "mobile" = #{mobile}
</if>
<if test="alliance_name != null and alliance_name !=''" >
and "driver_management_id" = #{alliance_name}
</if>
<if test="register_city != null and register_city !=''" >
and substr("register_city" , 0 , 4) = substr(#{register_city} , 0 , 4)
</if>
<if test="driver_type != null and driver_type !=''" >
and "driver_type" = #{driver_type}
</if>
GROUP BY
"driver_id" ,
"driver_name" ,
"mobile" ,
"alliance_name" ,
"register_city" ,
"driver_type"
union all
-- 他のUNION ALLセクションも同様の構造を持っています
) as TB
GROUP BY
TB."driver_id" ,
TB."driver_name" ,
TB."mobile" ,
TB."alliance_name" ,
TB."register_city" ,
TB."driver_type"
</select>
問題の分析
このコードの問題点は、MyBatisでUNION操作を実行すると、生成されるテーブル名とフィールド名は大文字小文字を区別しないが、Phoenixでは公式に大文字小文字を区別するという点にあります。
MyBatis統合後のUNIONで生成されたテーブル名とフィールド名には、大文字小文字を区別する必要がなく、かつ二重引用符を付けるべきではないため、二重引用符を付けるとフィールドが見つからないなどのエラーが発生します。
解決策
解決策は、最終的なクエリでのテーブルエイリアスとフィールド参照の方法を変更することです。以下が修正後のコードです:
<select id="searchContext" resultType="com.df.entity.SearchInfo">
SELECT
"TB1".driver_id,
"TB1".driver_name,
"TB1".mobile,
"TB1".alliance_name,
"TB1".register_city,
"TB1".driver_type,
SUM("TB1".pushOrderNum) AS "pushOrderNum" ,
SUM("TB1".effectiveSlogan) AS "effectiveSlogan",
SUM("TB1".grab_sus_order) AS "grab_sus_order",
-- 他のSUM関数も同様に修正
FROM
(
-- ここではUNION ALLセクションは同じですが、
-- 最終的なテーブルエイリアスを"TB1"としています
) AS "TB1"
GROUP BY
"TB1".driver_id ,
"TB1".driver_name ,
"TB1".mobile ,
"TB1".alliance_name ,
"TB1".register_city ,
"TB1".driver_type
</select>
まとめ
この問題は、Phoenixの大文字小文字を区別する特性とMyBatisが生成するSQLの間の不一致によって引き起こされました。解決策として、UNION操作で生成されたテーブルのフィールド参照方法を変更し、大文字小文字の区別を適切に処理することで問題を解決しました。
このケースから学んだ教訓は、異なるデータベース技術を統合する際には、それぞれの特性を深く理解し、特に大文字小文字の扱いに注意を払う必要があるということです。