暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle 使用带有聚合函数的JSON_TABLE

askTom 2017-09-27
1148

问题描述

嗨,团队

数据库版本: 12.2.0.0.3

在高水平上,成功完成脚本 (来自LiveSQL),您将拥有:
1) 在STAGE_USER表中以JSON格式加载10k个用户数据。
2) 在STAGE_ROLE表中以JSON格式加载500角色数据。
1) 在STAGE_USERMEM表中以JSON格式加载10k用户-角色成员身份数据。每个用户都100角色。

[注意] ==> 您可能会在语句 #9、19和29执行期间收到 “ORA-06502: PL/SQL: numeric or value error”,这将关系数据转换为JSON格式。请忽略错误,数据将被加载到各自的表中。

在成功执行脚本后创建的所有三个暂存表上收集统计信息。

EXEC dbms_stats.gather_table_stats(USER, '');


以下是我看到使用JSON_TABLE运算符的响应时间延迟的两个用例:

1) 在成员资格表中找到分配角色 “DummyRole1” 的用户总数

以下SQL的平均响应时间: 〜3.9秒。
在该成员关系表中,随着数据增加到100k,平均响应时间增加到20秒。

SELECT COUNT(1)
FROM STAGE_USERMEM o,
        json_table(userrolemem_json, '$'
    COLUMNS 
        (
           NESTED PATH '$.user_mem_dtls.user_role_mem[*]'
             COLUMNS (role_id VARCHAR2(32 CHAR) PATH '$.role_id',
                      role_name  VARCHAR2(20 CHAR) PATH '$.role_name')
        )
    ) t
WHERE t.role_name='DummyRole1';


2) 在成员资格表中找到分配给每个用户的角色总数

以下SQL的平均响应时间: 〜4.5秒。
在该成员关系表中,随着数据增加到100k,平均响应时间增加到30秒。

SELECT t.user_id,COUNT(1)
FROM STAGE_USERMEM o,
        json_table(userrolemem_json, '$'
    COLUMNS 
        (
           user_id VARCHAR2(32 CHAR) PATH '$.user_id',
           NESTED PATH '$.user_mem_dtls.user_role_mem[*]'
             COLUMNS (role_id VARCHAR2(32 CHAR) PATH '$.role_id',
                      role_name  VARCHAR2(20 CHAR) PATH '$.role_name')
        )
    ) t
GROUP BY t.user_id;


您能否建议如何加快上述两个用例的响应时间,因为当负载增加时,响应时间会增加。

提前谢谢!

专家解答

样本数据是否代表您的真实数据?

DummyRole1存在于每个JSON文档中。所以你要归还表上的每一行。

如果这只是一个 “例如”,并且在您的生产数据中,每个角色仅出现在 “几个” 文档中,则创建JSON搜索索引可能会带来一些好处。

为此,您首先需要在列中添加 “is json” 检查约束。然后添加索引:

alter table stage_usermem add constraint j check ( userrolemem_json is json );
create search index stage_usermem_ji on stage_usermem (userrolemem_json) for json;


但是,如果角色出现在大多数/所有文档中,添加这将带来很少或没有好处:

set serveroutput off
SELECT /*+ gather_plan_statistics */COUNT(1)
FROM STAGE_USERMEM o,
        json_table(userrolemem_json, '$'
    COLUMNS 
        (
           NESTED PATH '$.user_mem_dtls.user_role_mem[*]'
             COLUMNS (role_id    VARCHAR2(32 CHAR) PATH '$.role_id',
                      role_name  VARCHAR2(20 CHAR) PATH '$.role_name')
        )
    ) t
WHERE t.role_name='DummyRole1';

select * from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST'));

Plan hash value: 2625201623                                                                                   
                                                                                                              
------------------------------------------------------------------------------------------------------------  
| Id  | Operation               | Name          | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
------------------------------------------------------------------------------------------------------------  
|   0 | SELECT STATEMENT        |               |      1 |        |      1 |00:00:01.97 |   20134 |  20000 |  
|   1 |  SORT AGGREGATE         |               |      1 |      1 |      1 |00:00:01.97 |   20134 |  20000 |  
|   2 |   NESTED LOOPS          |               |      1 |  10000 |  10000 |00:00:02.01 |   20134 |  20000 |  
|   3 |    TABLE ACCESS FULL    | STAGE_USERMEM |      1 |  10000 |  10000 |00:00:00.02 |     134 |      0 |  
|   4 |    JSONTABLE EVALUATION |               |  10000 |        |  10000 |00:00:01.93 |   20000 |  20000 |  
------------------------------------------------------------------------------------------------------------  
                                                                                                              
Note                                                                                                          
-----                                                                                                         
   - statistics feedback used for this statement 

alter table stage_usermem add constraint j check ( userrolemem_json is json );
create search index stage_usermem_ji on stage_usermem (userrolemem_json) for json;

SELECT /*+ gather_plan_statistics */COUNT(1)
FROM   STAGE_USERMEM o,
      json_table(userrolemem_json, '$'
       COLUMNS 
        (
           NESTED PATH '$.user_mem_dtls.user_role_mem[*]'
             COLUMNS (role_id    VARCHAR2(32 CHAR) PATH '$.role_id',
                      role_name  VARCHAR2(20 CHAR) PATH '$.role_name')
        )
    ) t
WHERE t.role_name='DummyRole1';

select * from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST'));

Plan hash value: 3790423225                                                                                            
                                                                                                                       
---------------------------------------------------------------------------------------------------------------------  
| Id  | Operation                     | Name             | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
---------------------------------------------------------------------------------------------------------------------  
|   0 | SELECT STATEMENT              |                  |      1 |        |      1 |00:00:02.07 |   21148 |  20877 |  
|   1 |  SORT AGGREGATE               |                  |      1 |      1 |      1 |00:00:02.07 |   21148 |  20877 |  
|   2 |   NESTED LOOPS                |                  |      1 |    408 |  10000 |00:00:01.96 |   21148 |  20877 |  
|   3 |    TABLE ACCESS BY INDEX ROWID| STAGE_USERMEM    |      1 |      5 |  10000 |00:00:00.06 |    1147 |    877 |  
|*  4 |     DOMAIN INDEX              | STAGE_USERMEM_JI |      1 |        |  10000 |00:00:00.03 |    1020 |    877 |  
|   5 |    JSONTABLE EVALUATION       |                  |  10000 |        |  10000 |00:00:01.84 |   20001 |  20000 |  
---------------------------------------------------------------------------------------------------------------------  
                                                                                                                       
Predicate Information (identified by operation id):                                                                    
---------------------------------------------------                                                                    
                                                                                                                       
   4 - access("CTXSYS"."CONTAINS"("O"."USERROLEMEM_JSON",'{DummyRole1} INPATH                                          
              (/user_mem_dtls/user_role_mem/role_name)')>0)  


请注意,使用索引时,缓冲区列中的值会增加。所以这使得查询做更多的工作!

因此,如果角色是常见的,您可能更适合在查询中创建物化视图。然后查询。例如:

create materialized view json_mv
as
SELECT t.role_name, COUNT(1)
FROM STAGE_USERMEM o,
        json_table(userrolemem_json, '$'
    COLUMNS 
        (
           NESTED PATH '$.user_mem_dtls.user_role_mem[*]'
             COLUMNS (role_id    VARCHAR2(32 CHAR) PATH '$.role_id',
                      role_name  VARCHAR2(20 CHAR) PATH '$.role_name')
        )
    ) t
group by t.role_name;

select /*+ gather_plan_statistics */* from json_mv
where  role_name = 'DummyRole1';

select * from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST'));

Plan hash value: 445572365                                                                  
                                                                                            
------------------------------------------------------------------------------------------  
| Id  | Operation            | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
------------------------------------------------------------------------------------------  
|   0 | SELECT STATEMENT     |         |      1 |        |      1 |00:00:00.01 |       9 |  
|*  1 |  MAT_VIEW ACCESS FULL| JSON_MV |      1 |      1 |      1 |00:00:00.01 |       9 |  
------------------------------------------------------------------------------------------  
                                                                                            
Predicate Information (identified by operation id):                                         
---------------------------------------------------                                         
                                                                                            
   1 - filter("ROLE_NAME"='DummyRole1')                                                     
                                                                                            
Note                                                                                        
-----                                                                                       
   - dynamic statistics used: dynamic sampling (level=2)  


请注意,缓冲区 (工作) 已急剧下降。你也应该看到更快的执行时间。

当然,值得一问:

need要将数据存储为JSON?你可以用正则关系表代替吗?

这使得索引编制更加直接。

而且,如果您仔细查看原始计划,您会发现大多数时间都在进行 “JSONTABLE评估” 操作。即将数据从JSON转换为关系是大多数时间的地方。摆脱这一点,你可以让你的查询更快。
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论