【问题标题】:Sum of differences over a JSONB array field in CockroachDBCockroachDB 中 JSONB 数组字段的差异总和
【发布时间】:2019-03-07 15:43:19
【问题描述】:

我有一堆名为 text 的 jsonb 对象,它们都遵循名为 texts 的表中的相同结构,当使用以下查询选择时,它们看起来像这样:

从文本限制 1 中选择 jsonb_pretty(text);

[                                                                                                                                                                                                                                                                                                                                                                   
        [                                                                                                                                                                                                                                                                                                                                                               
            {                                                                                                                                                                                                                                                                                                                                                           
                "speaker": null,                                                                                                                                                                                                                                                                                                                                        
                "start": 0.02,                                                                                                                                                                                                                                                                                                                                      
                "stop": 2.25,                                                                                                                                                                                                                                                                                                                                       
                "text": "random text 123"                                                                                                                                                                                                                                                                                                     
            },                

            {                                                                                                                                                                                                                                                                                                                                                           
                "speaker": null,                                                                                                                                                                                                                                                                                                                                        
                "start": 291.45,                                                                                                                                                                                                                                                                                                                                    
                "stop": 291.88,                                                                                                                                                                                                                                                                                                                                     
                "text": "random text 123"                                                                                                                                                                                                                                                                                                                      
            },                                                                                                                                                                                                                                                                                                                                                          
            {                                                                                                                                                                                                                                                                                                                                                           
                "speaker": null,                                                                                                                                                                                                                                                                                                                                        
                "start": 292.07,                                                                                                                                                                                                                                                                                                                                    
                "stop": 293.63,                                                                                                                                                                                                                                                                                                                                     
                "text": "random text 123"                                                                                                                                                                                                                                                                                           
            }                                                                                                                                                                                                                                                                                                                                                           
       ],                                                                                                                                                                                                                                                                                                                                                              
  []                                                                                                                                                                                                                                                                                                                                                              
]  

我想总结表中 jsonb 对象中的所有差异(停止-启动)。

这样第一个对象的结果是:(2.25-0.02)+(291.88-291.45)+(293.63-292.07) = 4.22,然后需要将其与表中的其余条目相加。

我尝试了很多不同的查询对象的方法,但我找不到任何有效的方法,任何帮助或正确方向的指针将不胜感激。

【问题讨论】:

    标签: json cockroachdb


    【解决方案1】:

    适用于 CockroachDB 的版本(使用 v2.1.5 测试):

    CREATE TABLE test_data (
        id serial primary key,
        data jsonb not null
    );
    
    INSERT INTO test_data (data) VALUES
        ('[[{"speaker":null,"start":0.02,"stop":2.25,"text":"random text 123"},{"speaker":null,"start":291.45,"stop":291.88,"text":"random text 123"},{"speaker":null,"start":292.07,"stop":293.63,"text":"random text 123"}],[]]'::jsonb),
        ('[[{"speaker":null,"start":0.05,"stop":1.97,"text":"random text 123"},{"speaker":null,"start":260.78,"stop":261.23,"text":"random text 123"},{"speaker":null,"start":272.07,"stop":273.73,"text":"random text 123"}],[]]'::jsonb)
    ;
    
    SELECT test_data.id,
        sum((items.item->>'stop')::numeric - (items.item->>'start')::numeric)
    FROM test_data
    INNER JOIN (SELECT id, jsonb_array_elements(data#>'{0}'::string[]) AS item FROM test_data) AS items ON (items.id = test_data.id)
    GROUP BY 1
    ORDER BY 1;
    

    ...导致:

              id         | sum   
    +--------------------+------+
      432708358512836609 | 4.22  
      432708358512869377 | 4.03  
    (2 rows)
    

    解释:

                   tree               |    field    |    description     
    +---------------------------------+-------------+-------------------+
      sort                            |             |                    
       │                              | order       | +id                
       └── group                      |             |                    
            │                         | aggregate 0 | id                 
            │                         | aggregate 1 | sum(column6)       
            │                         | group by    | @2                 
            └── render                |             |                    
                 └── join             |             |                    
                      │               | type        | inner              
                      │               | equality    | (id) = (id)        
                      ├── project set |             |                    
                      │    └── scan   |             |                    
                      │               | table       | test_data@primary  
                      │               | spans       | ALL                
                      └── scan        |             |                    
                                      | table       | test_data@primary  
                                      | spans       | ALL                
    (17 rows)
    

    在 PostgreSQL 的全面支持下,您可以使用 LATERAL 连接和 GROUP BY ROLLUP 来获得总和:

    SELECT id, sum((items->'stop')::numeric - (items->'start')::numeric)
    FROM test_data, LATERAL jsonb_array_elements(data#>'{0}') AS items
    GROUP BY rollup(1)
    ORDER BY 1 NULLS LAST;
    

    ...结果:

     id | sum  
    ----+------
      1 | 4.22
      2 | 4.03
        | 8.25
    (3 rows)
    

    解释(实际上比 CockroachDB 中的简单,因为没有连接和按索引排序):

                                               QUERY PLAN                                            
    -------------------------------------------------------------------------------------------------
     GroupAggregate  (cost=0.16..4815.59 rows=1271 width=36)
       Group Key: test_data.id
       Group Key: ()
       ->  Nested Loop  (cost=0.16..2577.21 rows=127000 width=36)
             ->  Index Scan using test_data_pkey on test_data  (cost=0.15..37.20 rows=1270 width=36)
             ->  Function Scan on jsonb_array_elements items  (cost=0.01..1.00 rows=100 width=32)
    (6 rows)
    

    【讨论】:

    • 好的,在这种情况下,您应该在所有情况下将 PostgreSQL 替换为 CockroachDB。是的,它们从 PostgreSQL 继承了很多,但在这种情况下,它们不支持 LATERAL(有关详细信息,请参阅 SQL supportFROM 子句中的 set-returning 函数调用被隐式执行为 LATERAL)。
    • 另外,CockroachDB 似乎还不支持GROUP BY ROLLUP。我已经提供了另一个没有那个和没有LATERAL 加入的例子。这可能适用于您的数据库。
    • 我只是用那个把test_data分离出来作为一个临时表。因此,您只需将test_data 替换为实际的SELECT 并完全删除WITH
    • 这表明 CockroachDB 的解析器在这里有点不完整,因为它应该评估函数参数中表达式的返回类型,而不是将表达式本身作为参数类型,因为它应该支持#>操作。最后一次尝试:data#>'{0}'::text[]
    • 哇!魔鬼真的在细节中!好的,然后尝试使用(items.item->>'stop')::decimal(运算符->> 返回string,而-> 最初返回jsonb,这可能会让CockroachDB 感到困惑)。
    猜你喜欢
    • 1970-01-01
    • 2022-05-25
    • 2020-03-09
    • 1970-01-01
    • 2021-06-23
    • 1970-01-01
    • 2020-11-18
    • 2018-09-09
    • 2019-08-07
    相关资源
    最近更新 更多