2476


PostgreSQL9.3.3をCentOSで利用しています。

[code]

    CREATE TABLE latest_data (
        id bigserial primary key,
        observed_at timestamp,
        lat real,
        lon real,
        value real
    );
    
    CREATE INDEX latest_data_lat_lon_index ON latest_data(lat, lon);
    
[/code]

というテーブルがあった時、

[code]

    # SELECT * FROM latest_data OFFSET 0 LIMIT 1;
       id   |     observed_at     |  lat  |  lon   | value
    --------+---------------------+-------+--------+--------
     178576 | 2014-12-21 01:21:00 | 76.15 | 148.35 | 0.0078
    (1 row)
    
[/code]

と、データは入っているのですが、以下のようなクエリを発行すると、値が返ってきません。

[code]

    # SELECT * FROM latest_data WHERE lat = 76.15 AND lon = 148.35;
     id | observed_at | lat | lon | value
    ----+-------------+-----+-----+-------
    (0 rows)
    
[/code]

原因がわかれば教えていただけないでしょうか。


CC BY-SA 3.0
2014-12-24T06:18:17.607
2475
2014-12-24T06:18:17.607
351
question
sql postgresql
PostgreSQLでreal型のカラムを用いて検索をするには？


下記コマンドを動かせばわかるのですが、SQL文中の実数（0.15など）はデフォルトではnumeric型です。

[code]

    # SELECT 0.15 IS OF(real) AS is_real, 0.15 IS OF(numeric) AS is_numeric;
     is_real | is_numeric
    ---------+------------
     f       | t
    (1 row)
    
[/code]

ですので、以下のようにテーブル中のデータをnumeric型にキャストするか、

[code]

    # SELECT * FROM latest_data WHERE round(lat::numeric, 2) = 76.15 AND round(lon::numeric, 2) = 148.35;
       id   |     observed_at     |  lat  |  lon   | value
    --------+---------------------+-------+--------+-------
     178576 | 2014-12-22 00:27:00 | 76.15 | 148.35 | 0.001
    (1 row)
    
[/code]

以下のようにSELECT文中の実数をreal型にキャストして比較してください。

[code]

    # SELECT * FROM latest_data WHERE lat = 76.15::real AND lon = 148.35::real;
       id   |     observed_at     |  lat  |  lon   | value
    --------+---------------------+-------+--------+-------
     178576 | 2014-12-22 00:27:00 | 76.15 | 148.35 | 0.001
    (1 row)
    
[/code]

ただし、テーブル中のカラムをnumericにキャストするとINDEXが効きません。

[code]

    # EXPLAIN SELECT * FROM latest_data WHERE round(lat::numeric, 2) = 76.15 AND round(lon::numeric, 2) = 148.35;
                                           QUERY PLAN
    ----------------------------------------------------------------------------------------
     Seq Scan on latest_data  (cost=0.00..54206.33 rows=39 width=28)
       Filter: ((round((lat)::numeric, 2) = 76.15) AND (round((lon)::numeric, 2) = 148.35))
    (2 rows)
    
[/code]

のようにseq scanになります。

なので、SQL中の数値をreal型にキャストするべきでしょう。

[code]

    # EXPLAIN SELECT * FROM latest_data WHERE lat = 76.15::real AND lon = 148.35::real;
                                                       QUERY PLAN
    ----------------------------------------------------------------------------------------------------------------
     Index Scan using latest_data_lat_lon_index on latest_data  (cost=0.43..8.45 rows=1 width=28)
       Index Cond: ((lat = 76.15::real) AND (lon = 148.35::real))
    (2 rows)
    
[/code]

となり、INDEXが使われていることがわかります。

参考：[Value of real type incorrectly
compares](https://stackoverflow.com/questions/18236784/value-of-real-type-
incorrectly-compares)


CC BY-SA 3.0
2014-12-24T06:18:17.607
2476
2014-12-24T06:18:17.607
2017-05-23T12:38:55.307
-1
351
2475
answer