【问题标题】:How to convert PostgreSQL 9.4's jsonb type to float如何将 PostgreSQL 9.4 的 jsonb 类型转换为浮点型
【发布时间】:2014-07-18 13:37:53
【问题描述】:

我正在尝试以下查询:

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)?

【问题讨论】:

  • postgres 中默认没有try_cast 函数。你需要自己写。

标签: sql postgresql casting jsonb postgresql-9.4


【解决方案1】:

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

详情:JSON Functions and Operators

试试

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

【讨论】:

  • 可能你不想弄乱值所以使用 ::float + 0.0 AS lat,1 lat 变化是非常大的差异!
  • 为什么我需要添加 0.0(或在您的情况下为 1.0),它不是已经使用 ::float 输入浮点数了吗?
  • stackoverflow.com/a/62621483/287948所示,请更正为pg12.3+上的“直接演员”
【解决方案2】:

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

【讨论】:

【解决方案3】:

Per documentation,还有功能

jsonb_populate_record()
jsonb_populate_recordset()

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

json_populate_record()
json_populate_recordset()

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

CREATE TEMP TABLE x(lat <b>float</b>);

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

仅填充那些 namejson 对象中的 key 匹配的列。 value 被强制转换为 type 列,并且必须兼容,否则会引发异常。其他键被忽略。

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;

在此处使用implicit LATERAL JOIN

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

这在 Postgres 9.3 中的工作方式与 json 相同。还有一个额外的好处是,jsonb 中的数字数据不需要在内部转换为/从text

【讨论】:

  • JSONb 的优点是 NUMBER 和 BOOLEAN 二进制格式,不需要中间转换成 text。因此,这个答案是最好的,因为请记住一个好的做法:不要将 CPU 时间浪费在中间的 CAST 到文本上!。另一方面,不幸的是,PostgreSQL 开发人员 所做的仅此而已:关于 PostgreSQL 中“非冗余 CAST”的唯一 evidence 是在使用 jsonb_populate_record(或记录集)时。见dba.stackexchange.com/a/271249/90651
【解决方案4】:

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

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

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

如果你尝试这样做,它会失败:

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

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

【讨论】:

【解决方案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); 

子问题:

  • 可能从哪个版本开始?

  • 是语法糖还是real conversion

  • 如果真正的“二进制 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

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

【讨论】:

  • 似乎唯一的优化是使用jsonb_populate_record(另请检查jsonb_to_recordjsonb_to_recordset)。另请参阅this dba discussion
【解决方案6】:

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

试试这个:

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

【讨论】:

    【解决方案7】:

    创建视图时我使用了 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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-23
      相关资源
      最近更新 更多