Reputation: 31851
I have a JSONB column in my Postgres DB. In some cases it contains numbers encoded as strings*, such as: "123"
or "3.232"
or "2.32e14"
. The quotes are part of the stored JSONB value, it's "123"
not 123
.
I'd like to sum up these values, thus need a way to treat them as floating point. I cannot find any way in PSQL to do this.
select column::float
fails since they are JSON strings, not DB text type. Nor does column::text::float
work.
select to_number(column::text, '999.999')
doesn't work since to_number
requires a fixed format of number and I can't find a way to parse a standard formatted floating point number.
How can I convert the JSONB string-encoded values into a numeric value for computations?
*Background: They are stored as strings because storage needs to be exact and does not survive a round-trip to/from a float point. For calculations it's okay to reduce precision to float. They could survive a round-trip to a high-level decimal format, but it's a limitation of the Python library SQLAlchemy with JSON that prevents this from working. Even if we find a solution here, I still need to migrate existing data, thus the question stands.
Upvotes: 1
Views: 2762
Reputation: 31851
This syntax converts the type to an unescaped JSON value and then casts to a float:
(value#>>'{}')::float
For example, in a sum:
WITH d(json) AS (
VALUES ('"123"'::jsonb)
, ('"3.232"')
, ('"2.32e14"')
)
SELECT SUM((json#>>'{}')::float)
FROM d;
This achieves the same result as the answer from @Marth. I'm uncertain which is the more correct, performant, or preferred approach.
Upvotes: 2
Reputation: 24802
Assuming you're using postgresql 12+, you can use the .double()
jsonpath method for this:
WITH d(json) AS (
VALUES ('"123"'::jsonb)
, ('"3.232"')
, ('"2.32e14"')
)
SELECT json, JSONB_TYPEOF(json), jsonb_path_query(json, '$.double()')
FROM d;
json | jsonb_typeof | jsonb_path_query |
---|---|---|
123 | string | 123 |
3.232 | string | 3.232 |
2.32e14 | string | 232000000000000 |
Since the path
here only ever returns a single double
the jsonb_path_query_first()
function is more appropriate, as the jsonb_path_query()
returns a set of result (usually used in a subquery):
WITH d(json) AS (
VALUES ('"123"'::jsonb)
, ('"3.232"')
, ('"2.32e14"')
)
SELECT SUM(jsonb_path_query_first(json, '$.double()')::float)
FROM d;
sum |
---|
232000000000126.22 |
Upvotes: 2