【问题标题】:How to cast / parse JSON striing as number / float in Postgres?如何在 Postgres 中将 JSON 字符串转换为数字/浮点数?
【发布时间】:2021-02-19 19:47:46
【问题描述】:

我的 Postgres 数据库中有一个 JSONB 列。在某些情况下,它包含编码为字符串*的数字,例如: "123""3.232""2.32e14"。引号是存储的 JSONB 值的一部分,它是 "123" 而不是 123

我想总结这些值,因此需要一种方法将它们视为浮点数。我在 PSQL 中找不到任何方法来执行此操作。

select column::float 失败,因为它们是 JSON 字符串,而不是 DB 文本类型。 column::text::float 也不起作用。

select to_number(column::text, '999.999') 不起作用,因为to_number 需要固定格式的数字,而我找不到解析标准格式浮点数的方法。

如何将 JSONB 字符串编码的值转换为数字值以进行计算?


*背景:它们被存储为字符串,因为存储需要精确,并且不能在往返浮点数的往返过程中存活。对于计算,可以将精度降低到浮点数。他们可以在往返于高级十进制格式的过程中幸存下来,但这是 Python 库 SQLAlchemy 与 JSON 的限制,它阻止了它的工作。即使我们在这里找到了解决方案,我仍然需要迁移现有数据,因此问题存在。

【问题讨论】:

    标签: postgresql


    【解决方案1】:

    假设您使用的是 postgresql 12+,您可以为此使用 .double() jsonpath 方法:

    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

    View on DB Fiddle


    由于这里的path 只返回一个double,所以jsonb_path_query_first() 函数更合适,因为jsonb_path_query() 返回一组结果(通常在子查询中使用):

    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

    【讨论】:

    • 我不确定返回的是什么类型,但结果似乎无法用于sum 等计算。也就是说,select sum(jsonb_path_query(json, '$.double())) 不起作用。 aggregate function calls cannot contain set-returning function calls。所以我假设路径函数是一个聚合,而不是一个值。
    • @edA-qamort-ora-y:你说得对,jsonb_path_query 返回一个集合,因此大多数情况下并不意味着直接在SELECT 子句中使用。我主要用它来展示$.double() jsonpath 可以工作。我添加了一个使用jsonb_path_query_first() 的示例,在这里效果更好,因为它返回单个结果。
    • 啊,好吧。我尝试了 ::float 演员表,但函数的 _first 变体是让它工作的诀窍。
    • 我也找到了替代解决方案,请参阅我的答案。你知道哪种方法更有意义吗?
    【解决方案2】:

    此语法将类型转换为未转义的 JSON 值,然后强制转换为浮点数:

    (value#>>'{}')::float

    例如,总和:

    WITH d(json) AS (
        VALUES ('"123"'::jsonb)
             , ('"3.232"')
             , ('"2.32e14"')
    )
    SELECT SUM((json#>>'{}')::float)
    FROM d;
    

    这达到了与@Marth 的答案相同的结果。我不确定哪种方法更正确、更高效或更受欢迎。

    【讨论】:

    • 我不会猜到“当前路径的值”(因为您可以将查询编写为 json#>>(ARRAY[]::text[]) 并带有要查找的路径的数组)会提取文本值本身并使其castable,但它是有道理的。我在回答中看到的主要区别是非数字值的错误(通用转换错误,例如“双精度类型的输入语法无效”与“jsonpath 项方法 .double() 只能应用于字符串或数值” )。我不能说哪个更快,尽管我猜它是你的。无论如何,很高兴为 postgresql
    猜你喜欢
    • 2011-11-25
    • 1970-01-01
    • 2018-02-15
    • 2011-12-18
    • 1970-01-01
    • 1970-01-01
    • 2012-05-21
    相关资源
    最近更新 更多