WordPressで小数点の比較がうまくいかない場合
WordPressのMeta Queryで小数を比較するときの注意点について説明します。数字だからとりあえず「NUMERIC」を「type」に指定している場合は要注意です。
ほかの数字を扱う「SIGNED」「DECIMAL」などと比較しながらどのように取得すれば、小数でも意図したデータの取得ができるのか見ていきましょう。
Meta Queryによって生成されるSQL
Meta QueryでどのようにWHERE文が作成されるのかを確認しておきます。作成されたWHERE文を検証の際にも利用します。
下記はmeta_keyが「sample_meta_key」でmeta_valueが1.0以上のものを取得するものです。
$args = ['meta_query' => [['key' => 'sample_meta_key','value' => '1.0','type' => 'NUMERIC', // SIGNED DECIMAL'compare' => '>=',],],];$query = new WP_Query( $args );
NUMERIC
( wp_postmeta.meta_key = ‘sample_meta_key’ AND CAST(wp_postmeta.meta_value AS SIGNED) >= ‘1.0’ )
SIGNED
( wp_postmeta.meta_key = ‘sample_meta_key’ AND CAST(wp_postmeta.meta_value AS SIGNED) >= ‘1.0’ )
UNSIGNED
( wp_postmeta.meta_key = ‘sample_meta_key’ AND CAST(wp_postmeta.meta_value AS UNSIGNED) >= ‘1.0’ )
DECIMAL
( wp_postmeta.meta_key = ‘sample_meta_key’ AND CAST(wp_postmeta.meta_value AS DECIMAL) >= ‘1.0’ )
検証用のデータ
それぞれの比較や正しく比較できているかを検証するようのデータを下記のようにします。point列はwp_postmetaテーブルのmeta_valueと同じくlongtext型を設定しています。
+-------+| point |+-------+| 1.5 || 1.6 || -1.5 || -1.6 || 2 || 3 || 2.5 || -5.0 || 1.0 |+-------+
うまくいくパターンとうまくいかないパターン
基本的に上で紹介したパターンの場合うまくいくかないケースが多いですが、場合によってはうまくいきます。検証用のデータをMeta Queryで生成されるSQLを使用して取得して確認してみます。
うまくいくパターン
比較対象の数値が「1.0」や「2.0」など小数でも整数でも変わらない場合、影響が少ないです。下記の場合、想定している通りの結果を得ることができます。
SELECT point FROM test WHERE CAST(point AS SIGNED) >= '1.0' ORDER BY point ASC
+-------+| point |+-------+| 1.0 || 1.5 || 1.6 || 2 || 2.5 || 3 |+-------+
うまくいかないパターン
比較対象の数値が「1.1」など小数の場合想定してない結果となってしまいます。
SIGNEDの場合「1.5」と「1.6」が取得できていません。DECIMALの場合「1.5」と「1.6」を取得してしまっています。
SELECT point FROM test WHERE CAST(point AS SIGNED) >= '1.1' ORDER BY point ASCSELECT point from test WHERE CAST(point AS DECIMAL) >= '1.8' ORDER BY point ASC
+-------+| point |+-------+| 2 || 2.5 || 3 |+-------+
+-------+| point |+-------+| 1.5 || 1.6 || 2 || 2.5 || 3 |+-------+
うまくいかない原因
なぜ上記のやり方は失敗するのでしょうか。
それはCASTの型が間違っているからです。
SIGNEDでは符号付の整数にキャストされます。その結果「1.5」は「1」、「1.6」も「1」になります。「1.0以上」では取得でき、「1.1」以上で取得できないのはこういうことです。
-- result 1SELECT CAST('1.1' AS SIGNED)
DECIMALの場合小数点第一位が四捨五入された整数にキャストされます。その結果「1.5」は2、「1.6」も2となります。よって「1.8以上」でも「1.5」「1.6」が取得されてしまいます。
-- result 2SELECT CAST('1.5' AS DECIMAL)
どうすれば意図した結果となるか
意図した結果とするにはやはりキャストが必要です。上記のDECIMALの方法に少し手を加えると意図した結果となります。
DECIMALは引数をとることができます。DECIMAL(5,2)とした場合、最大桁数が5、小数点の桁数が「2」となります。(-999.99 ~ 999.99)
上記のパターンに当てはめてみます。
SELECT point from test WHERE CAST(point AS DECIMAL(3,2)) >= '1.8' ORDER BY point ASC;
+-------+| point |+-------+| 2 || 2.5 || 3 |+-------+
無事意図した形で取得できています。「1.8」以上で取得されてしまっていた「1.5」や「1.6」が排除されています。
これをMeta Queryで利用すると下記のようになります。利用する数値によって桁数や小数点桁数を変更してください。
$args = ['meta_query' => [['key' => 'sample_meta_key','value' => '1.0','type' => 'DECIMAL(3,2)','compare' => '>=',],],];$query = new WP_Query( $args );
まとめ
WordPressの公式サイトなどを見ると、使用できる値に「‘NUMERIC’, ‘BINARY’, ‘CHAR’, ‘DATE’, ‘DATETIME’, ‘DECIMAL’, ‘SIGNED’, ‘TIME’, ‘UNSIGNED’」となっています。
数値系全部試したけどうまくいかず、解決に結構時間がかかったのでまとめました。
今回はサンプルのため「>=」を条件として利用していますが、「BETWEEN」などを使用するとたまたまうまくいくことがなく、一目瞭然で結果がおかしいことに気づけます。