55

我正在尝试以下查询:

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

(+1.0 只是为了强制转换为浮动。我的实际查询要复杂得多,这个查询只是问题的一个测试用例。)

我得到错误:

ERROR:  operator does not exist: jsonb + numeric

如果我添加显式转换:

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

错误变为:

ERROR:  operator does not exist: jsonb + double precesion

我知道大多数 jsonb 值不能转换为浮点数,但在这种情况下,我知道纬度都是 JSON 数字。

是否有一个函数可以将 jsonb 值转换为浮点数(或为不可转换返回 NULL)?

4

7 回答 7

121

有两种操作可以从中获取值JSON。第一个->将返回JSON。第二个->>将返回文本。

详细信息:JSON 函数和运算符

尝试

SELECT (json_data->'position'->>'lat')::float + 1.0 AS lat
FROM updates
LIMIT 5
于 2014-07-18T15:00:06.900 回答
13

AFAIK 在 Postgres 中没有 json->float 转换,所以你可以尝试显式(json_data->'position'->'lat')::text::float转换

于 2014-07-18T13:41:55.940 回答
6

根据文档,还有功能

jsonb_populate_record()
jsonb_populate_recordset()

类似于他们的 json 双胞胎(从第 9.3 页开始出现)

json_populate_record()
json_populate_recordset()

您需要预定义的行类型。要么使用现有表的行类型,要么使用CREATE TYPE. 或者用临时表替代:

CREATE TEMP TABLE x(lat float);

可以是单个列或一长列的列。

仅填充那些名称与对象中的匹配的列json。该被强制转换为列类型,并且必须兼容,否则会引发异常。其他键被忽略。

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;

在这里使用隐式LATERAL JOIN

同样,用于jsonb_populate_recordset()将数组分解为每个条目的多行。

这在 Postgres 9.3 中的工作方式与json. 还有一个额外的好处是,text对于jsonb.

于 2014-07-28T18:58:47.647 回答
5

现在我们可以做到了!

现在我们可以直接从 JSONb 转换为 SQL 数据类型。我正在使用 PostgreSQL v12.3,它工作正常:

SELECT (j->'i')::int, (j->>'i')::int, (j->'f')::float, (j->>'f')::float
FROM  (SELECT '{"i":123,"f":12.34}'::jsonb) t(j); 

子问题:

  • 可以从哪个版本开始?

  • 它是语法糖还是真正的转换

  • 如果是真正的“二进制 JSONb → 二进制 SQL”转换,微优化在哪里?
    例如,什么会比“二进制 JSONb → 字符串 → 二进制 SQL”更快(?)?boolean→boolean, number→numeric, number→int, number→bigint; 数字→浮点数,数字→双精度。

  • 为什么不针对 NULL 进行优化?
    奇怪的是,“NULL 到 SqlType”不起作用,“错误:无法将 jsonb null 转换为整数类型”。


基准建议

如何检查?PostgreSQL 何时优化循环查询?

EXPLAIN ANALYSE SELECT (j->'i')::int, (j->'f')::float       -- bynary to bynary INT and FLOAT
-- EXPLAIN ANALYSE SELECT (j->>'i')::int, (j->>'f')::float  -- string to bynary INT and FLOAT

-- EXPLAIN ANALYSE SELECT (j->'i')::numeric, (j->'f')::numeric    -- bynary to bynary NUMERIC
-- EXPLAIN ANALYSE SELECT (j->>'i')::numeric, (j->>'f')::numeric  -- string to bynary NUMERIC

FROM (
  SELECT (('{"i":'||x||',"f":'||x||'.34}')::jsonb) as j FROM  generate_series(1,599999) g(x)
  -- SELECT (('{"i":123,"f":12.34}')::jsonb) as j FROM  generate_series(1,599999) g(x)
) t;

PostgreSQL 错误?

即使是现在,2021 版本 pg13 版本......不强制转换 NULL 没有意义:自然是强制转换NULL::int为整数,但 PostgreSQL 在自动强制转换中失败:

SELECT (j->'i')::int FROM  (SELECT '{"i":null}'::jsonb) t(j); -- fail

导致"ERROR: cannot cast jsonb null to type integer"

于 2020-06-28T10:50:25.093 回答
5

添加说明,因为这是“JSONB 浮点转换”搜索的热门搜索 - 请注意,您需要将 JSON 转换包装在括号中,然后应用“::”转换。

如上所述,正确的方法是:

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

相反,如果您尝试这样做,它将失败:

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

这是我在代码中犯的错误,我花了一段时间才看到它——一旦我注意到就很容易修复。

于 2018-11-05T22:27:16.847 回答
1

您必须将 json 值转换为文本,然后再转换为浮动。

尝试这个:

(json_data #>> '{field}')::float
于 2017-03-06T20:48:43.337 回答
0

创建视图时,我使用了 CAST:

create view mydb.myview as
            select id,
            config->>'version' as version,
            config->>'state' as state,
            config->>'name' as name,
            config->>'internal-name' as internal_name,
            config->>'namespace' as namespace,         
            create_date,
            update_date,
            CAST(config ->> 'version' as double precision) as version_number
            from mydb.mytbl;
于 2020-04-03T20:15:44.773 回答