How to convert PostgreSQL 9.4's jsonb type to

2019-02-05 23:40发布

问题:

I'm trying the following query:

SELECT (json_data->'position'->'lat') + 1.0 AS lat FROM updates LIMIT 5;

(The +1.0 is just there to force conversion to float. My actual queries are far more complex, this query is just a test case for the problem.)

I get the error:

ERROR:  operator does not exist: jsonb + numeric

If I add in explicit casting:

SELECT (json_data->'position'->'lat')::float + 1.0 AS lat FROM updates LIMIT 5;

the error becomes:

ERROR:  operator does not exist: jsonb + double precesion

I understand that most jsonb values cannot be cast into floats, but in this case I know that the lats are all JSON numbers.

Is there a function which casts jsonb values to floats (or return NULLs for the uncastable)?

回答1:

There are two operations to get value from JSON. The first one -> will return JSON. The second one ->> will return text.

Details: JSON Functions and Operators

Try

SELECT (json_data->'position'->>'lat')::float + 1.0 AS lat
FROM updates
LIMIT 5


回答2:

Per documentation, there are also the functions

jsonb_populate_record()
jsonb_populate_recordset()

Analog to their json twins (present since pg 9.3)

json_populate_record()
json_populate_recordset()

You need a predefined row type. Either use the row-type of an existing table or define one with CREATE TYPE. Or substitute with a temporary table ad hoc:

CREATE TEMP TABLE x(lat float);

Can be a single column or a long list of columns.

Only those columns are filled, where the name matches a key in the json object. The value is coerced to the column type and has to be compatible or an exception is raised. Other keys are ignored.

SELECT lat + 1  -- no need for 1.0, this is float already
FROM   updates u
     , jsonb_populate_record(NULL::x, u.json_data->'position')
LIMIT  5;

Using an implicit LATERAL JOIN here.

Similarly, use jsonb_populate_recordset() to decompose arrays into multiple rows per entry.

This works the same way in Postgres 9.3 with json. There is the added benefit that casting to / from text internally is not necessary for numeric data in jsonb.



回答3:

AFAIK there's no json->float casting in Postgres, so you could try an explicit (json_data->'position'->'lat')::text::float cast



回答4:

You must to cast the json value to text and then to float.

Try this:

(json_data #>> '{field}')::float


回答5:

Adding a clarification because this comes up as the top hit for a 'JSONB float conversion' search - note that you need to wrap the JSON conversion in brackets, and then apply the '::' casting.

As mentioned above, the correct method is:

(json_data #>> '{field}')::float

If instead you try this it will fail:

json_data #>> '{field}'::float

This was the mistake I was making in my code and it took me a while to see it - easy fix once I noticed.