ITPub博客

首页 > 数据库 > Oracle > [20230413]慎用nvarchar2数据类型2.txt

[20230413]慎用nvarchar2数据类型2.txt

原创 Oracle 作者:lfree 时间:2023-04-17 20:39:29 0 删除 编辑

[20230413]慎用nvarchar2数据类型2.txt

--//生产系统不同数据库一套字符串使用varchar2类型,另外一套使用nvarchar2类型导致的问题.通过真实的例子说明问题.

1.环境:
> @ver1
PORT_STRING         VERSION    BANNER
------------------- ---------- ----------------------------------------------------------------------------
x86_64/Linux 2.4.xx 11.2.0.4.0 Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

> @ ashtop sql_id sql_id='by086v1989xs8' &day
    Total                                                                         Distinct Distinct
  Seconds     AAS %This   SQL_ID        FIRST_SEEN          LAST_SEEN           Execs Seen  Tstamps
--------- ------- ------- ------------- ------------------- ------------------- ---------- --------
    24392      .3  100% | by086v1989xs8 2023-04-13 02:45:10 2023-04-13 03:29:20       3722     2571

> @ sqlhh by086v1989xs8 1
BEGIN_INTERVAL_TIME INST_ID SQL_ID        PLAN_HASH_VALUE EXECUTIONS ELA_MS_PER_EXEC CPU_MS_PER_EXEC ROWS_PER_EXEC LIOS_PER_EXEC BLKRD_PER_EXEC IOW_MS_PER_EXEC  AVG_IOW_MS CLW_MS_PER_EXEC APW_MS_PER_EXEC CCW_MS_PER_EXEC
------------------- ------- ------------- --------------- ---------- --------------- --------------- ------------- ------------- -------------- --------------- ----------- --------------- --------------- ---------------
2023-04-12 14:00:20       1 by086v1989xs8       701626669       1350            7162            6697          47.7        635680         635971             223         0.0               0               1               0
2023-04-12 15:00:16       1 by086v1989xs8       701626669        770            7576            7136          45.4        640502         640710             215         0.0               0               1               0
2023-04-13 02:00:20       1 by086v1989xs8       701626669       1299            6486            6031          47.2        625700         626143             246         0.0               0               1               0
2023-04-13 03:00:16       1 by086v1989xs8       701626669       2429            6660            6143          45.8        633385         633582             249         0.0               0               1               0

2.分析:
> @ sql_id by086v1989xs8
--SQL_ID = by086v1989xs8
SELECT
b.id as report_id,/*检验报告ID*/
to_char(a.zyh) as patient_id,/*病人ID*/
c.examinaim as report_name,/*报告名称*/
c.requesttime as pms_oder_date,/*开检验单日期*/
c.receivetime as accept_date,/*送检日期*/
b.audit_time as report_date,/*报告日期*/
b.sample_type_name as sample,/*样本*/
c.stayhospitalmode as zyflag,/*检验数据类型 (0 普通、1 急诊 、2 疼痛中心、3、血透、4 早癌筛查)*/
b.RESULT_FINISH_TIME as pms_result_date,/*出结果日期*/
:"SYS_B_00" as patient_id1,/*病人ID1*/
:"SYS_B_01" as patient_id2,/*病人ID2*/
:"SYS_B_02" as patient_id3,/*病人ID3*/
:"SYS_B_03" as report_id1,/*检验报告ID1*/
:"SYS_B_04" as report_id2,/*检验报告ID2*/
:"SYS_B_05" as report_id3,/*检验报告ID3*/
:"SYS_B_06" as pms_location,/*采样地点*/
c.requesttime as pms_sample_date,/*样本收集日期*/
c.requestmode as pms_exam_type,/*检查类型*/
:"SYS_B_07" as pms_result_loc,/*检验科还是即时检测*/
:"SYS_B_08" as pms_battery,/*一组检验项目编码*/
b.depart_name as pms_dept/*科室*/
FROM zy_brry a
inner join lis_test@HLXJY b on a.mzhm = b.pat_id
inner join l_lis_sqd  c on b.barcode = c.tmbh
where :"SYS_B_09"=:"SYS_B_10"
and a.zyh = :Paitent_id;
--//仔细检查发现各表对应索引都建立,nested连接应该没有问题.

> @ dpcawr by086v1989xs8 ''
Plan hash value: 701626669
-----------------------------------------------------------------------------------------------
|Id|Operation                      |Name      |E-Rows|E-Bytes|Cost(%CPU)|E-Time  |Inst |IN-OUT|
-----------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT               |          |      |       | 173K(100)|        |     |      |
| 1| FILTER                        |          |      |       |          |        |     |      |
| 2|  HASH JOIN                    |          |   16 |  3648 | 173K  (1)|00:34:48|     |      |
| 3|   NESTED LOOPS                |          |    6 |  1032 |   6   (0)|00:00:01|     |      |
| 4|    TABLE ACCESS BY INDEX ROWID|ZY_BRRY   |    1 |    21 |   3   (0)|00:00:01|     |      |
| 5|     INDEX UNIQUE SCAN         |PK_ZY_BRRY|    1 |       |   2   (0)|00:00:01|     |      |
| 6|    REMOTE                     |LIS_TEST  |    6 |   906 |   3   (0)|00:00:01|HLXJY| R->S |
| 7|   TABLE ACCESS STORAGE FULL   |L_LIS_SQD |   24M|  1307M| 173K  (1)|00:34:47|     |      |
-----------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$9E43CB6E
   4 - SEL$9E43CB6E / A@SEL$1
   5 - SEL$9E43CB6E / A@SEL$1
   6 - SEL$9E43CB6E / B@SEL$1
   7 - SEL$9E43CB6E / C@SEL$2
Peeked Binds (identified by position):
--------------------------------------
  12 - :PAITENT_ID (CHAR(30), CSID=852): '20041093'
Remote SQL Information (identified by operation id):
----------------------------------------------------
   6 - SELECT "ID","PAT_ID","DEPART_NAME","AUDIT_TIME","BARCODE","RESULT_FINISH_TIME","SAMPLE_TYPE_NAM
       E" FROM "LIS_TEST" "B" WHERE "PAT_ID"=:1 (accessing 'HLXJY' )
--//使用dpc.sql看不到执行计划(说明已经不再共享池),只能使用dbms_xplan.display_awr查看.许多信息不全.
--//id=7全表全表扫描,为什么?

--//我自己生成脚本执行得到的执行计划如下:
Plan hash value: 701626669
------------------------------------------------------------------------------------------------------------------------------------------------------------
|Id|Operation                      |Name      |Starts|E-Rows|E-Bytes|Cost(%CPU)|E-Time  |Inst |IN-OUT|A-Rows|   A-Time  |Buffers|Reads|OMem |1Mem |Used-Mem|
------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT               |          |     1|      |       | 173K(100)|        |     |      |  240 |00:00:11.76|   647K| 647K|     |     |        |
|*1| FILTER                        |          |     1|      |       |          |        |     |      |  240 |00:00:11.76|   647K| 647K|     |     |        |
|*2|  HASH JOIN                    |          |     1|   16 |  3648 | 173K  (1)|00:34:48|     |      |  240 |00:00:11.76|   647K| 647K|1968K|1968K|1237K(0)|
| 3|   NESTED LOOPS                |          |     1|    6 |  1032 |   6   (0)|00:00:01|     |      |  261 |00:00:00.01|     4 |   0 |     |     |        |
| 4|    TABLE ACCESS BY INDEX ROWID|ZY_BRRY   |     1|    1 |    21 |   3   (0)|00:00:01|     |      |    1 |00:00:00.01|     4 |   0 |     |     |        |
|*5|     INDEX UNIQUE SCAN         |PK_ZY_BRRY|     1|    1 |       |   2   (0)|00:00:01|     |      |    1 |00:00:00.01|     3 |   0 |1025K|1025K|        |
| 6|    REMOTE                     |LIS_TEST  |     1|    6 |   906 |   3   (0)|00:00:01|HLXJY| R->S |  261 |00:00:00.01|     0 |   0 |     |     |        |
| 7|   TABLE ACCESS STORAGE FULL   |L_LIS_SQD |     1|   24M|  1307M| 173K  (1)|00:34:47|     |      |   24M|00:00:02.99|   647K| 647K|1025K|1025K|3085K(0)|
------------------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$9E43CB6E
   4 - SEL$9E43CB6E / A@SEL$1
   5 - SEL$9E43CB6E / A@SEL$1
   6 - SEL$9E43CB6E / B@SEL$1
   7 - SEL$9E43CB6E / C@SEL$2
Outline Data
-------------
/*+
    BEGIN_OUTLINE_DATA
    IGNORE_OPTIM_EMBEDDED_HINTS
    OPTIMIZER_FEATURES_ENABLE('11.2.0.4')
    DB_VERSION('11.2.0.4')
    ALL_ROWS
    OUTLINE_LEAF(@"SEL$9E43CB6E")
    MERGE(@"SEL$58A6D7F6")
    OUTLINE(@"SEL$3")
    OUTLINE(@"SEL$58A6D7F6")
    MERGE(@"SEL$1")
    OUTLINE(@"SEL$2")
    OUTLINE(@"SEL$1")
    INDEX_RS_ASC(@"SEL$9E43CB6E" "A"@"SEL$1" ("ZY_BRRY"."ZYH"))
    FULL(@"SEL$9E43CB6E" "B"@"SEL$1")
    FULL(@"SEL$9E43CB6E" "C"@"SEL$2")
    LEADING(@"SEL$9E43CB6E" "A"@"SEL$1" "B"@"SEL$1" "C"@"SEL$2")
    USE_NL(@"SEL$9E43CB6E" "B"@"SEL$1")
    USE_HASH(@"SEL$9E43CB6E" "C"@"SEL$2")
    END_OUTLINE_DATA
*/
Peeked Binds (identified by position):
--------------------------------------
   3 - (VARCHAR2(30), CSID=852): '861636'
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(:SYS_B_09=:SYS_B_10)
   2 - access("B"."BARCODE"=SYS_OP_C2C("C"."TMBH"))
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   5 - access("A"."ZYH"=TO_NUMBER(:PAITENT_ID))
Remote SQL Information (identified by operation id):
----------------------------------------------------
   6 - SELECT "ID","PAT_ID","DEPART_NAME","AUDIT_TIME","BARCODE","RESULT_FINISH_TIME","SAMPLE_TYPE_NAME" FROM "LIS_TEST" "B" WHERE "PAT_ID"=:1 (accessing 'HLXJY' )
--//问题在于id=7,选择全表扫描.为什么?

> @seg2 PPPPPP_HHH.L_LIS_SQD
SEG_MB OWNER      SEGMENT_NAME SEG_PART_NAME SEGMENT_TYPE SEG_TABLESPACE_NAME BLOCKS HDRFIL  HDRBLK
------ ---------- ------------ ------------- ------------ ------------------- ------ ------ -------
  5070 PPPPPP_HHH L_LIS_SQD                  TABLE        PPPPPP_HHH          648960     35 1147425
--//噢5G,开发应该好好感谢exadata,它才是真正的先进工作者!!
--//仔细看id=2的Predicate Information 信息,连接条件存在一个函数是SYS_OP_C2C.
   2 - access("B"."BARCODE"=SYS_OP_C2C("C"."TMBH"))

SYS@192.168.100.235:1521/orcl> @ desczz lis.lis_test barcode
eXtended describe of lis.lis_test

DISPLAY TABLE_NAME OF COLUMN_NAME INFORMATION.
INPUT   OWNER.TABLE_NAME  <filters>
SAMPLE  : @ desczz TABLE_NAME column_name1,column_name2
IF NOT INPUT <filters> ,USE "" .

Owner Table_Name SAMPLE_SIZE LAST_ANALYZED       Col# Column Name Null?    Type          NUM_DISTINCT      Density NUM_NULLS HISTOGRAM NUM_BUCKETS Low_value  High_value
----- ---------- ----------- ------------------- ---- ----------- -------- ------------- ------------ ------------ --------- --------- ----------- ---------- ----------
LLL   LIS_TEST          8755 2022-11-01 09:10:12   55 BARCODE     NOT NULL NVARCHAR2(28)      9530368 .00000000000         0 HYBRID            254 0018153755 _

> @ desczz PPPPPP_HHH.l_lis_sqd tmbh
eXtended describe of PPPPPP_HHH.l_lis_sqd
DISPLAY TABLE_NAME OF COLUMN_NAME INFORMATION.
INPUT   OWNER.TABLE_NAME  <filters>
SAMPLE  : @ desczz TABLE_NAME column_name1,column_name2
IF NOT INPUT <filters> ,USE "" .

Owner      Table_Name SAMPLE_SIZE LAST_ANALYZED       Col# Column Name Null? Type         NUM_DISTINCT      Density  NUM_NULLS HISTOGRAM NUM_BUCKETS Low_value High_value
---------- ---------- ----------- ------------------- ---- ----------- ----- ------------ ------------ ------------ ---------- --------- ----------- --------- ----------
PPPPPP_HHH L_LIS_SQD     23112823 2023-03-20 00:00:22   30 TMBH              VARCHAR2(30)     23164928 .00000004317    1360529                     1 00160721  9500000111
--//仔细看两个连接字段的类型不一致,导致出现SYS_OP_C2C转换,出现全表扫描.也就是存在隐式转换,修改如下:

$ cat by086v1989xs8.sql9d_0
variable SYS_B_09 NUMBER
variable SYS_B_10 NUMBER
variable PAITENT_ID VARCHAR2(32)
begin
:SYS_B_09 := 1;
:SYS_B_10 := 1;
:PAITENT_ID := '861636';
null;
end;
/
set termout off
set sqlblanklines on
alter session set current_schema=PPPPPP_HHH;
alter session set statistics_level=all;

SELECT
b.id as report_id,/*检验报告ID*/
to_char(a.zyh) as patient_id,/*病人ID*/
c.examinaim as report_name,/*报告名称*/
c.requesttime as pms_oder_date,/*开检验单日期*/
c.receivetime as accept_date,/*送检日期*/
b.audit_time as report_date,/*报告日期*/
b.sample_type_name as sample,/*样本*/
c.stayhospitalmode as zyflag,/*检验数据类型 (0 普通、1 急诊 、2 疼痛中心、3、血透、4 早癌筛查)*/
b.RESULT_FINISH_TIME as pms_result_date,/*出结果日期*/
--:"SYS_B_00" as patient_id1,/*病人ID1*/
--:"SYS_B_01" as patient_id2,/*病人ID2*/
--:"SYS_B_02" as patient_id3,/*病人ID3*/
--:"SYS_B_03" as report_id1,/*检验报告ID1*/
--:"SYS_B_04" as report_id2,/*检验报告ID2*/
--:"SYS_B_05" as report_id3,/*检验报告ID3*/
--:"SYS_B_06" as pms_location,/*采样地点*/
c.requesttime as pms_sample_date,/*样本收集日期*/
c.requestmode as pms_exam_type,/*检查类型*/
--:"SYS_B_07" as pms_result_loc,/*检验科还是即时检测*/
--:"SYS_B_08" as pms_battery,/*一组检验项目编码*/
b.depart_name as pms_dept/*科室*/
FROM zy_brry a
inner join lis_test@HLXJY b on a.mzhm = b.pat_id
inner join l_lis_sqd  c on cast(b.barcode as varchar2(30)) = c.tmbh
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
where :"SYS_B_09"=:"SYS_B_10"
and a.zyh = :Paitent_id;
set termout on
set sqlblanklines off
--@zws '' ''
--@dpc '' ''
@dpc '' outline
rollback;
alter session set current_schema=SYS ;
--//当然还有1个思路就是建立SYS_OP_C2C("TMBH")的函数索引.
--//补充说明,还可以加入如下条件:
inner join l_lis_sqd  c on cast(b.barcode as varchar2(30)) = c.tmbh and b.barcode = cast (c.tmbh as nvarchar2(28))

Plan hash value: 3494871177
--------------------------------------------------------------------------------------------------------------------------------------------------------------
|Id|Operation                       |Name            |Starts|E-Rows|E-Bytes|Cost (%CPU)|E-Time  |Inst |IN-OUT|A-Rows|   A-Time  |Buffers|OMem |1Mem |Used-Mem|
--------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT                |                |     1|      |       |   24 (100)|        |     |      |   240|00:00:00.01|    606|     |     |        |
|*1| FILTER                         |                |     1|      |       |           |        |     |      |   240|00:00:00.01|    606|     |     |        |
| 2|  NESTED LOOPS                  |                |     1|     7|  1596 |   24   (0)|00:00:01|     |      |   240|00:00:00.01|    606|     |     |        |
| 3|   NESTED LOOPS                 |                |     1|     7|  1596 |   24   (0)|00:00:01|     |      |   240|00:00:00.01|    536|     |     |        |
| 4|    NESTED LOOPS                |                |     1|     6|  1032 |    6   (0)|00:00:01|     |      |   261|00:00:00.01|      4|     |     |        |
| 5|     TABLE ACCESS BY INDEX ROWID|ZY_BRRY         |     1|     1|    21 |    3   (0)|00:00:01|     |      |     1|00:00:00.01|      4|     |     |        |
|*6|      INDEX UNIQUE SCAN         |PK_ZY_BRRY      |     1|     1|       |    2   (0)|00:00:01|     |      |     1|00:00:00.01|      3|1025K|1025K|        |
| 7|     REMOTE                     |LIS_TEST        |     1|     6|   906 |    3   (0)|00:00:01|HLXJY| R->S |   261|00:00:00.01|      0|     |     |        |
|*8|    INDEX RANGE SCAN            |I_L_LIS_SQD_TMBH|   261|     1|       |    2   (0)|00:00:01|     |      |   240|00:00:00.01|    532|1025K|1025K|        |
| 9|   TABLE ACCESS BY INDEX ROWID  |L_LIS_SQD       |   240|     1|    56 |    3   (0)|00:00:01|     |      |   240|00:00:00.01|     70|     |     |        |
--------------------------------------------------------------------------------------------------------------------------------------------------------------

3.再次提醒:
--//建议开发不要拿着怎么国际化需求吓唬人,其实根本不了解这种类型的存储模式,也就是不要乱用数据类型.许多老系统(我相信大部分还是使用varchar2类型).
--//那套新数据库只要字符类型都是nvarchar2类型,实际上我看到仅仅是冰山一角,应用中涉及到这样dblink的语句基本都有问题.

> @ ashtable '' @HLXJY 1=1 &day
@ tpt/ashtop username,sql_id,module "1=1 and sql_id in (select sql_id from v$sqlarea where lower(sql_fulltext) like lower('%@HLXJY%'))"  sysdate-1 sysdate
    Total                                                                                               Distinct Distinct
  Seconds     AAS %This   USERNAME   SQL_ID        MODULE     FIRST_SEEN          LAST_SEEN           Execs Seen  Tstamps
--------- ------- ------- ---------- ------------- ---------- ------------------- ------------------- ---------- --------
    78556      .9   49% | PPPPPP_HHH by086v1989xs8 w3wp.exe   2023-04-17 02:45:33 2023-04-17 05:08:04      12017     8303
    69744      .8   43% | PPPPPP_HHH 1h1b8ccmaxpx1 w3wp.exe   2023-04-17 05:31:44 2023-04-17 07:37:52      24982     7340
    12426      .1    8% | PPPPPP_HHH 5dxqqv937z5a7 w3wp.exe   2023-04-17 05:08:27 2023-04-17 05:31:24       9068     1339
        1      .0    0% | PPPPPP_HHH 00rgs3nb3czcw PPPPPP.EXE 2023-04-17 08:01:23 2023-04-17 08:01:23          1        1
--//前面3条基本都是一样的问题.

$ cat ashtable.sql
column module format a30
prompt
prompt @ tpt/&&1.ashtop username,sql_id,module "&&3 and sql_id in (select sql_id from v$sqlarea where lower(sql_fulltext) like lower('%&&2%'))"  &&4 &&5
prompt
@ tpt/&&1.ashtop username,sql_id,module "&&3 and sql_id in (select sql_id from v$sqlarea where lower(sql_fulltext) like lower('%&&2%'))"  &&4 &&5

--//简单说明:
--//参数1 '' d ,''表示执行ashtop,d 表示执行dashtop.
--//参数2 查询相关的表或者特定字符串
--//参数3 过滤查询条件
--//参数4,5 查询时间范围.我一般选择查询trunc(sysdate-1) sysdate,这样简单一些.

--//查询使用DBA_HIST_SQLTEXT视图呢?
> @ tpt/dashtop username,sql_id,module "1=1 and sql_id in (select sql_id from DBA_HIST_SQLTEXT where lower(sql_text) like lower('%@HLXJY%'))"  sysdate-1 sysdate
    Total
  Seconds     AAS %This   USERNAME   SQL_ID        MODULE   FIRST_SEEN          LAST_SEEN
--------- ------- ------- ---------- ------------- -------- ------------------- -------------------
    78700      .9   49%   PPPPPP_HHH by086v1989xs8 w3wp.exe 2023-04-17 02:45:36 2023-04-17 05:07:55
    70020      .8   43%   PPPPPP_HHH 1h1b8ccmaxpx1 w3wp.exe 2023-04-17 05:31:45 2023-04-17 07:37:49
    12700      .1    8%   PPPPPP_HHH 5dxqqv937z5a7 w3wp.exe 2023-04-17 05:08:36 2023-04-17 05:31:15

--//我曾经帮别人优化过一个ms sqlsever的项目,对方讲分开执行很快,而连接在一起执行很慢,仔细检查发现也是类似的问题.
--//后来我给他讲了问题出在字符类型的定义上,实际上在于开发管理的混乱,一部分人使用varchar2类型,另外一部分人使用nvarchar2类型.
--//后来对方花了一个晚上的时间统一到nvarchar2类型,实际上这套新系统我猜测也是类似的情况,一部分人使用varchar2类型,另外一部分人使用nvarchar2类型.
--//等到上线发现性能问题,全部修改为nvarchar2类型,从程序带入的绑定变量参数类型就可以知道确定,两者是混用的.只不过这时转换发生绑定变量端.

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/267265/viewspace-2946379/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
熟悉oracle相关技术,擅长sql优化,rman备份与恢复,熟悉linux shell编程。

注册时间:2008-01-03

  • 博文量
    4006
  • 访问量
    7970808