如何将 PostgreSQL 9.4 的 jsonb 类型转换为浮点数

How to convert PostgreSQL 9.4's jsonb type to float

提问人:fadedbee 提问时间:7/18/2014 最后编辑:Clodoaldo Netofadedbee 更新时间:11/17/2021 访问量:90761

问:

我正在尝试以下查询:

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 值不能转换为浮点数,但在这种情况下,我知道 lats 都是 JSON 数字。

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

sql postgresql 强制转换 jsonb postgresql-9.4

评论

0赞 Ihor Romanchenko 7/18/2014
postgres 中默认没有函数。你需要自己写。try_cast
0赞 Bergi 7/7/2022
相关新闻: 如何将 Postgres json(b) 转换为整数?, to text?, to boolean?

答:

18赞 knitti 7/18/2014 #1

AFAIK Postgres 中没有 json->float 强制转换,因此您可以尝试显式强制转换(json_data->'position'->'lat')::text::float

评论

0赞 Peter Krauss 5/22/2021
在 pg12.3+ 上,您可以进行“直接投射”,如 stackoverflow.com/a/62621483/287948 所示
139赞 Ihor Romanchenko 7/18/2014 #2

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

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

尝试

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

评论

2赞 Abhijit Gujar 1/18/2018
可能你不想弄乱价值,所以使用 ::float + 0.0 AS lat,1 lat 变化是非常大的区别!
2赞 n3rd 1/11/2020
为什么我需要添加 0.0(或 1.0),它不是已经使用 ::float 打字到浮点数中了吗?
0赞 Peter Krauss 5/22/2021
请更正为 pg12.3+ 上的“直接投射”,如 stackoverflow.com/a/62621483/287948 所示
0赞 Gaurav 3/23/2022
完美无缺地访问内部元素
7赞 Erwin Brandstetter 7/29/2014 #3

每个文档,还有函数

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 中的工作方式相同。还有一个额外的好处,即对于 中的数字数据,不需要在内部强制转换。jsontextjsonb

评论

1赞 Peter Krauss 5/22/2021
JSONb 的优点是 NUMBER 和 BOOLEAN 二进制格式,不需要中间转换为文本。所以,这个答案是最好的,因为记住一个好的做法:不要把你的CPU时间浪费在中间的CAST to text!上。另一方面,不幸的是,PostgreSQL开发人员所做的仅此而已:关于PostgreSQL中“非冗余CAST”的唯一证据是在使用(或记录集)时。查看 dba.stackexchange.com/a/271249/90651jsonb_populate_record
1赞 Luis Castillo 3/7/2017 #4

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

试试这个:

(json_data #>> '{field}')::float
5赞 rocksteady 11/6/2018 #5

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

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

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

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

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

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

评论

1赞 Peter Krauss 5/22/2021
在 pg12.3+ 上,您可以进行“直接投射”,如 stackoverflow.com/a/62621483/287948 所示(如下图)
0赞 The Code Guy 4/4/2020 #6

创建视图时,我使用了 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;
11赞 4 revsPeter Krauss #7

现在我们可以做到了!

如今,我们可以直接从 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”换算,哪里有微优化呢?
    例如,什么是更快的(?) tham “binary JSONb → string → binary SQL”?boolean→boolean, number→numeric, number→int, number→bigint;number→flloat,number→double。

  • 为什么不针对 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 没有意义:自然是强制转换为整数,但 PostgreSQL 在自动强制转换中失败:NULL::int

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

导致“错误:无法将 jsonb null 转换为整数类型”。

评论

0赞 Peter Krauss 9/17/2020
似乎唯一的优化是 (check also or )。另请参阅此 dba 讨论jsonb_populate_recordjsonb_to_recordjsonb_to_recordset
1赞 Bergi 7/7/2022
"不能将 jsonb null 转换为整数类型“不是错误。也不能将对象或数组强制转换为整数。使用 或NULLIF(j->'i', 'null')::int(j->>'i')::int
0赞 Peter Krauss 2/22/2023
@Bergi,我使用其他观点。似乎是规范错误(不是实现错误),而且是老式的。投射项目,例如 是原子的,是不是铸造复杂的对象。 不是一个错误,因为编译器最友好:所以,也必须友好......我们正处于 2023 年,ChatGPT 的时代,而不是编译器的石器时代:必须友好。x[1]::intselect NULL::int;select (j->'i')::int