ORACLE中record、varray、table、%type、%rowtype的使用详解

ORACLE中record、varray、table和%type、%rowtype的使用详解

2015年05月24日 18:14:42 X-rapido 阅读数:7585 标签: recordvarraytable数据库oracle 更多

个人分类: 数据库

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiaokui_wingfly/article/details/45953633

更早原文1

https://blog.csdn.net/liangweiwei130/article/details/38223319

下面在更早原文1有追加

查看原文:http://www.ibloger.net/article/230.html

1     说明

1.1       RECORD

 定义记录数据类型。它类似于C语言中的结构数据类型(STRUCTURE),PL/SQL提供了将几个相关的、分离的、基本数据类型的变量组成一个整体的方法,即RECORD复合数据类型。在使用记录数据类型变量时,需要在声明部分先定义记录的组成、记录的变量,然后在执行部分引用该记录变量本身或其中的成员。

 定义记录数据类型的语法如下:

TYPE RECORD_NAME IS RECORD(

V1  DATA_TYPE1 [NOT NULL][:=DEFAULT_VALUE],

V2  DATA_TYPE2 [NOT NULL][:=DEFAULT_VALUE],

VN  DATA_TYPEN [NOT NULL][:=DEFAULT_VALUE]);

1.2       VARRAY

 数组是具有相同数据类型的一组成员的集合。每个成员都有一个唯一的下标,它取决于成员在数组中的位置。在PL/SQL中,数组数据类型是VARRAY(variable array,即可变数组)。

 定义VARRAY数据类型的语法如下:

 TYPE VARRAY_NAMEIS VARRAY(SIZE) OF ELEMENT_TYPE [NOT NULL];

 其中,varray_name是VARRAY数据类型的名称,size是正整数,表示可以容纳的成员的最大数量,每个成员的数据类型是element_typeo默认时,成员可以取空值,否则需要使用NOT NULL加以限制。

1.3       TABLE

 定义记录表(或索引表)数据类型。它与记录类型相似,但它是对记录类型的扩展。它可以处理多行记录,类似于C语言中的二维数组,使得可以在PL/SQL中模仿数据库中的表。

 定义记录表类型的语法如下:

 TYPE TABLE NAME IS TABLE OF ELEMENT_TYPE [NOT NULL]

 INDEX BY [BINARY_INTEGER|PLS_INTEGER|VARRAY2];

 关键字INDEX BY表示创建一个主键索引,以便引用记录表变量中的特定行。

 BINARY_INTEGER的说明

 如语句:TYPE NUMBERS  IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;其作用是,加了”INDEX BYBINARY_INTEGER ”后,NUMBERS类型的下标就是自增长,NUMBERS类型在插入元素时,不需要初始化,不需要每次EXTEND增加一个空间。

 而如果没有这句话“INDEXBY BINARY_INTEGER”,那就得要显示对初始化,且每插入一个元素到NUMBERS类型的TABLE中时,都需要先EXTEND。

2     举例

2.1       创建表结构以及数据准备

 
  1. --组织机构结构表

  2. CREATE TABLE SF_ORG

  3. (

  4. ORG_ID INT NOT NULL, --组织机构主键ID

  5. ORG_NAME VARCHAR2(50),--组织机构名称

  6. PARENT_ID INT--组织机构的父级

  7. )

  8.  
  9. --一级组织机构

  10. INSERT INTO SF_ORG(ORG_ID, ORG_NAME, PARENT_ID) VALUES(1, '一级部门1',0);

  11.  
  12. --二级部门

  13.  
  14. INSERT INTO SF_ORG(ORG_ID, ORG_NAME, PARENT_ID) VALUES(2, '二级部门2',1);

  15. INSERT INTO SF_ORG(ORG_ID, ORG_NAME, PARENT_ID) VALUES(3, '二级部门3',1);

  16. INSERT INTO SF_ORG(ORG_ID, ORG_NAME, PARENT_ID) VALUES(4, '二级部门4',1);

2.2       RECORD的使用举例

先定义一个只与SF_ORG表中某几个列的数据类型相同的记录数据类型TYPE_ORG_RECORD,然后声明一个该数据类型的记录变量V_ORG_RECORD,最后用替换变量&ORG_ID接受输入的雇员编码,查询并显示该雇员的这几列中的信息。注意,在使用RECORD数据类型的变量时要用“.”运算符指定记录变量名限定词。

 一个记录类型的变量只能保存从数据库中查询出的一行记录,如果查询出了多行记录,就会出现错误。

 
  1. -- RECORD的使用举例

  2. declare

  3. type type_org_record is record(

  4. v_name sf_org.org_name%type,

  5. v_parent sf_org.parent_id%type);

  6.  
  7. v_record type_org_record;

  8. begin

  9. select org_name, parent_id into v_record from sf_org so

  10. where so.org_id = &org_id;

  11. dbms_output.put_line('部门名称:' || v_record.v_name);

  12. dbms_output.put_line('上级部门编码:' || to_char(v_record.v_parent));

  13. end;

执行时会弹出一个输入框,如下图,执行完毕以后可以在输出中查看效果


2.3       VARRAY的使用举例

先定义一个能保存5个VARCHAR2(25)数据类型的成员的VARRAY数据类型ORG_VARRAY_TYPE,然后声明一个该数据类型的VARRAY变量V_ORG_VARRAY,最后用与ORG_VARRAY_TYPE数据类型同名的构造函数语法给V_ORG_VARRAY变量赋予初值并显示赋值结果。

注意,在引用数组中的成员时.需要在一对括号中使用顺序下标,下标从1开始而不是从0开始。

 
  1. -- VARRAY的使用举例

  2. declare

  3. type org_varray_type is varray(5) of varchar2(25);

  4. v_arr_set org_varray_type;

  5. begin

  6. v_arr_set := org_varray_type('1','2','3','4','5');

  7. dbms_output.put_line('输出1:' || v_arr_set(1) || '、'|| v_arr_set(2) || '、'|| v_arr_set(3) || '、'|| v_arr_set(4));

  8. dbms_output.put_line('输出2:' || v_arr_set(5));

  9. v_arr_set(5) := '5001';

  10. dbms_output.put_line('输出3:' || v_arr_set(5));

  11. end;

2.4       TABLE使用举例

2.4.1      存储单列多行

这个和VARRAY类似。但是赋值方式稍微有点不同,不能使用同名的构造函数进行赋值。具体的如下:

 
  1. -- 存储单列多行

  2. declare

  3. type org_table_type is table of varchar2(25)

  4. index by binary_integer;

  5. v_org_table org_table_type;

  6. begin

  7. v_org_table(1) := '1';

  8. v_org_table(2) := '2';

  9. v_org_table(3) := '3';

  10. v_org_table(4) := '4';

  11. v_org_table(5) := '5';

  12. dbms_output.put_line('输出1:' || v_org_table(1) || '、'|| v_org_table(2) || '、'|| v_org_table(3) || '、'|| v_org_table(4)||'、'|| v_org_table(5));

  13. end;

2.4.2      存储多列多行和ROWTYPE结合使用

采用bulkcollect可以将查询结果一次性地加载到collections中。而不是通过cursor一条一条地处理。

 
  1. -- 存储多列多行和rowtype结合使用

  2. declare

  3. type t_type is table of sf_org%rowtype;

  4. v_type t_type;

  5. begin

  6. select org_id, org_name, parent_id bulk collect into v_type from sf_org where sf_org.org_id <= 3;

  7.  
  8. for v_index in v_type.first .. v_type.last loop

  9. dbms_output.put_line(v_type(v_index).org_id ||' '|| v_type(v_index).org_name ||' '|| v_type(v_index).parent_id );

  10. end loop;

  11. end;

2.4.3      存储多列多行和RECORD结合使用

采用bulkcollect可以将查询结果一次性地加载到collections中。而不是通过cursor一条一条地处理。

 
  1. -- 存储多列多行和RECORD结合使用

  2. declare

  3. type test_emp is record

  4. (

  5. c1 sf_org.org_name%type,

  6. c2 sf_org.parent_id%type

  7. );

  8. type t_type is table of test_emp;

  9. v_type t_type;

  10. begin

  11. select org_name, parent_id bulk collect into v_type from sf_org where sf_org.org_id <= 3;

  12.  
  13. for v_index in v_type.first .. v_type.last loop

  14. dbms_output.put_line(v_type(v_index).c1 || ' ' || v_type(v_index).c2);

  15. end loop;

  16. end;

3     问题

varry和table集合不能直接对其进行查询。只能对其进行遍历。

4    其他

  在我的Oracle 创建 split 和 splitstr 函数文章中,有包含table的查询示例图:http://blog.csdn.net/xiaokui_wingfly/article/details/45922141

%TYPE说明

为了使一个变量的数据类型与另一个已经定义了的变量(尤其是表的某一列)的数据类型相一致,Oracle提供了%TYPE定义方式。当被参照的那个变量的数据类型改变了之后,这个新定义的变量的数据类型会自动跟随其改变,容易保持一致,也不用修改PL/SQL程序了。当不能确切地知道被参照的那个变量的数据类型时,就只能采用这种方法定义变量的数据类型。

%ROWTYP说明

如果一个表有较多的列,使用%ROWTYPE来定义一个表示表中一行记录的变量,比分别使用%TYPE来定义表示表中各个列的变量要简洁得多,并且不容易遗漏、出错。这样会增加程序的可维护性。

 为了使一个变量的数据类型与一个表中记录的各个列的数据类型相对应、一致,Oracle提供%ROWTYPE定义方式。当表的某些列的数据类型改变了之后,这个新定义的变量的数据类型会自动跟随其改变,容易保持一致,也不用修改PL/SQL程序了。当不能确切地知道被参照的那个表的结构及其数据类型时,就只能采用这种方法定义变量的数据类型。

一行记录可以保存从一个表或游标中查询到的整个数据行的各列数据。一行记录的各个列与表中一行的各个列有相同的名称和数据类型。

参考文章:http://blog.csdn.net/liangweiwei130/article/details/38223319

https://blog.csdn.net/xiaokui_wingfly/article/details/45953633

%type、%rowtype,varry、record、table 的使用详解

2018年08月02日 23:20:30 鱼丸丶粗面 阅读数:106

 版权声明:转载、讨论时,请在明显位置注明出处。 https://blog.csdn.net/qq_34745941/article/details/81368648

0、基础数据准备

CREATE TABLE stu(
  ID NUMBER(3) PRIMARY KEY,
  xm NVARCHAR2(30)
)
INSERT INTO stu(ID, xm) VALUES(1, '小游子');
INSERT INTO stu(ID, xm) VALUES(2, '小倩子');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1、基础数据类型定义

1.1 %type

单条记录的数据类型定义

v_id System.stu.id%type; 

好处:若 id 字段类型改变,v_id 字段类型也会自动跟着改变
  • 1
  • 2
  • 3

1.2 %rowtype

所有记录的数据类型定义

v_row System.stu%rowtype;

好处:v_row 中所有的字段类型始终与 stu 表中的字段那类型保持一致。
  • 1
  • 2
  • 3

1.3 record

%type 是声明 单条记录的数据类型,%rowtype 是声明 所有记录的数据类型,如果想 声明多个数据类型(但不是 所有数据类型),该怎么办了?此时就用 record

DECLARE
        TYPE type_stu_record IS RECORD(
             v_id system.stu.id%TYPE,
             v_xm system.stu.xm%TYPE
        );
        v_stu_record type_stu_record;
  BEGIN
        SELECT t.id,         -- 类型,个数都必须与声明时,保持一致
               t.xm
          INTO v_stu_record 
          FROM stu t
         WHERE ROWNUM = 1;   -- 如果想 赋值多条记录,请看 集合数据类型定义
        dbms_output.put_line(v_stu_record.v_id ||' : '||v_stu_record.v_xm);
EXCEPTION WHEN OTHERS THEN
          dbms_output.put_line(Sqlerrm);
    END;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

1、集合数据类型定义

基本数据类型,select into 时,只能一条条插入记录 
集合数据类型,可使用 select bulk collect into 一次性插入所有数据

1.1 varry

指定长度的数组,下标从 1 开始。遍历输出所有项

type 数组名 varray(size) of 元素类型 [not null];

size : 数组长度,必填项。

DECLARE 
          TYPE type_var_stu IS VARRAY(4) OF VARCHAR2(30);
         v_arr type_var_stu;
  BEGIN
         v_arr := type_var_stu('a','b','c','d');
         dbms_output.put_line('输出第一个:'||v_arr(3));
         v_arr(3) := 'dd';    -- 下标3,必须已存在值,否则报错。
         dbms_output.put_line('输出第三个:'||v_arr(3));

           FOR v_index IN v_arr.first .. v_arr.last LOOP 
               -- 循环遍历数组
               dbms_output.put_line(v_arr(v_index));
           END LOOP;
EXCEPTION WHEN OTHERS THEN
          dbms_output.put_line(sqlerrm);
    END;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

1.2 table

type table_name is table of element_type[not null]
index by [binary_integer|pls_integer|varray2];

index by : 创建一个主键索引,以便引用记录表变量中的特定行。
binary_integer : 下标自增(无需‘初始化’)
  • 1
  • 2
  • 3
  • 4
  • 5

1.3.1 存储单行单列

DECLARE
        TYPE type_table_stu IS TABLE OF VARCHAR2(30)
       INDEX BY BINARY_INTEGER;

        v_t  type_table_stu;
  BEGIN
        v_t(1) := '1';
        v_t(2) := '2';
        v_t(3) := '3';

        dbms_output.put_line('输出第一个: '||v_t(1));
EXCEPTION WHEN OTHERS THEN
          dbms_output.put_line(Sqlerrm);
    END;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

1.3.2 存储多行多列与rowtype联用

要插入的列与表数据类型完全一致

DECLARE 
        TYPE type_table_stu IS TABLE OF system.stu%ROWTYPE;
        v_stu type_table_stu;
  BEGIN
        SELECT t.id,    -- 插入的值,个数、类型 都必须和声明时完全一致。
               t.xm
          BULK COLLECT  -- 一次性提取数据至集合内
          INTO v_stu
          FROM stu t;

           FOR v_index IN v_stu.first .. v_stu.last LOOP
              dbms_output.put_line(v_stu(v_index).id||' : '||v_stu(v_index).xm);
          END LOOP;

EXCEPTION WHEN OTHERS THEN 
          dbms_output.put_line(Sqlerrm);
    END;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

1.3.3 存储多行多列与record联用

DECLARE 
        TYPE type_record_stu IS RECORD( 
             v_id system.stu.id%TYPE,
             v_xm system.stu.xm%TYPE
        );
        TYPE type_table_stu IS TABLE OF type_record_stu; -- 将记录作为 table 的数据类型
        v_stu type_table_stu;
  BEGIN
        SELECT t.id,
               t.xm
          BULK COLLECT
          INTO v_stu
          FROM stu t;

            FOR v_index IN v_stu.first .. v_stu.last LOOP
                dbms_output.put_line(v_stu(v_index).v_id||' : '||v_stu(v_index).v_xm);
            END LOOP;

EXCEPTION WHEN OTHERS THEN 
          dbms_output.put_line(Sqlerrm);
    END;

https://blog.csdn.net/qq_34745941/article/details/81368648

oracle 复合类型-record、数组、%type、%rowtype、plsql table类型

oracleoracl复合类型详解 

知识点: 
1,ORACLE 在 PL/SQL 中除了提供象前面介绍的各种类型外,还提供一种称为复合类型的类型--- 
   记录和表. 
   记录类型类似于C语言中的结构数据类型,它把逻辑相关的、分离的、基本数据类型的变量组成一个整体存储起来,它必须包括至少一个标量型或RECORD 数据类型的成员,称作PL/SQL RECORD 的域(FIELD),其作用是存放互不相同但逻辑相关的信息。在使用记录数据类型变量时,需要先在声明部分先定义记录的组成、记录的变量,然后在执行部分引用该记录变量本身或其中的成员。 
定义记录类型语法如下: 
Java代码 

 收藏代码

  1. TYPE record_name IS RECORD(  
  2.    v1 data_type1 [NOT NULL] [:= default_value ],  
  3.    v2 data_type2 [NOT NULL] [:= default_value ],  
  4.    ......                       [color=red]//注意末尾不能带逗号[/color]  
  5.    vn data_typen [NOT NULL] [:= default_value ] );  


实例1: 
Java代码 

 收藏代码

  1. declare  
  2.    type test_rec is record(  
  3.       name varchar2(30) not null := '胡勇',  
  4.       info varchar2(100)  --该处末尾不能添加逗号  
  5.    );  
  6.    rec_book  test_rec;  
  7. begin  
  8.    rec_book.name := '陈超阳';  
  9.    rec_book.info := '中华人民共和国';  
  10.    dbms_output.put_line(rec_book.name || ',' || rec_book.info);  
  11. end;   



实例2: 
Java代码 

 收藏代码

  1. declare  
  2.   --定义与hr.employees表中的这几个列相同的记录数据类型  
  3.   type record_type_emp is record(  
  4.       v_ename   emp.ename%type,  
  5.       v_job     emp.job%type,  
  6.       v_sal     emp.sal%type  
  7.   );  
  8.   --声明一个该记录数据类型的记录变量  
  9.   v_emp_record record_type_emp;  
  10. begin  
  11.    --注意查询的类型应该与复合变量成员的顺序一致  
  12.   select ename,job,sal into v_emp_record  from emp where empno = 7369;  
  13.   dbms_output.put_line(v_emp_record.v_ename);      
  14.   dbms_output.put_line(v_emp_record.v_job);      
  15.   dbms_output.put_line(v_emp_record.v_sal);  
  16. end;     

  
输出如下: 
SMITH 
CLERK 
800 

实例3: 
Java代码 

 收藏代码

  1. declare  
  2.    type type_mytype is record(  
  3.        ename  scott.emp.ename%type,  
  4.        job    scott.emp.job%type,  
  5.        sal    scott.emp.sal%type  
  6.    );  
  7.    v_mytype type_mytype;  
  8. begin  
  9.   select e.ename,e.job,e.sal into v_mytype from scott.emp e where empno = '7499';  
  10.   dbms_output.put_line(v_mytype.ename);  
  11. end;  


注意:一个记录类型的变量只能保存从数据库中查询出的一行记录,若查询出了多行记录,就会出现错误。否则会报错: 
ORA-01422: 实际返回的行数超出请求的行数 
ORA-06512: 在 line 11 


第二种复合类型--数组 
    数据是具有相同数据类型的一组成员的集合。每个成员都有一个唯一的下标,它取决于成员在数组中的位置。在PL/SQL中,数组数据类型是VARRAY。 

定义VARRY数据类型语法如下: 
Java代码 

 收藏代码

  1. TYPE varray_name IS VARRAY(size) OF element_type [NOT NULL];  


varray_name是VARRAY数据类型的名称,size是下整数,表示可容纳的成员的最大数量,每个成员的数据类型是element_type。默认成员可以取空值,否则需要使用NOT NULL加以限制。对于VARRAY数据类型来说,必须经过三个步骤,分别是:定义、声明、初始化。 
实例1: 
Java代码 

 收藏代码

  1. --定义数组类型  
  2. declare  
  3.   --定义一个最多保存5个varchar(25)数据类型成员的varray数据类型  
  4.   type reg_varray_type is varray(5) of varchar(25);  
  5.   --声明一个该varray数据类型的变量  
  6.   v_reg_varray reg_varray_type;  
  7. begin  
  8.   --用构造函数赋予初值(注意赋值方式--构造函数)  
  9.  [color=red] --访问数组是从下标1开始的(不是0)[/color]  
  10.   v_reg_varray := reg_varray_type('中国','美国','英国','日本','河南');  
  11.   dbms_output.put_line(v_reg_varray(1));  
  12.   dbms_output.put_line(v_reg_varray(2));  
  13.   dbms_output.put_line(v_reg_varray(3));  
  14.   dbms_output.put_line(v_reg_varray(4));  
  15.   dbms_output.put_line(v_reg_varray(5));  
  16. end;  



实例2: 
--数组 varray实例 
declare 
   type type_myVarray is varray(3) of Varchar2(30); 
   v_myvarrayType  type_myVarray; 
begin 
   --第一种赋值方法:构造函数赋值 
   v_myvarrayType := type_myVarray('男人','女人','不男不女'); 
   dbms_output.put_line(v_myvarrayType(1)); 
   dbms_output.put_line(v_myvarrayType(2)); 
   dbms_output.put_line(v_myvarrayType(3)); 
   --第二种赋值方法:一个一个的赋值 
   select e.ename,e.job,to_char(e.sal) into v_myvarrayType(1),v_myvarrayType(2),v_myvarrayType(3)  from scott.emp e where empno = 7369; 
   --这种方式不行:会报错:---PLS-00642: 在 SQL 语句中不允许使用本地收集类型(这种赋值方式只适应与record类型的变量) 
   --select e.ename,e.job,to_char(e.sal) into v_myvarrayType from scott.emp e where empno = 7369; 
   dbms_output.put_line(v_myvarrayType(1)); 
   dbms_output.put_line(v_myvarrayType(2)); 
   dbms_output.put_line(v_myvarrayType(3)); 
end; 


第三种类型:%type 
    定义一个变量,其数据类型与已经定义的某个数据变量(尤其是表的某一列)的数据类型相一致,这时可以使用%TYPE。 
    使用%TYPE特性的优点在于:  
       所引用的数据库列的数据类型可以不必知道;  
       所引用的数据库列的数据类型可以实时改变,容易保持一致,也不用修改PL/SQL序。 
实例: 
Java代码 

 收藏代码

  1. DECLARE  
  2.   -- 用%TYPE 类型定义与表相配的字段  
  3.   TYPE T_Record IS RECORD(  
  4.        T_no emp.empno%TYPE,   
  5.        T_name emp.ename%TYPE,   
  6.        T_sal emp.sal%TYPE );  
  7.    -- 声明接收数据的变量  
  8.     v_emp T_Record;   
  9. BEGIN   
  10.     SELECT empno, ename, sal INTO v_emp FROM emp WHERE empno=7788;       
  11.     DBMS_OUTPUT.PUT_LINE(TO_CHAR(v_emp.t_no)||' '||v_emp.t_name||' ' ||      
  12.     TO_CHAR(v_emp.t_sal));  
  13. end;  
  14.     



第四种类型:%rowtype 
    PL/SQL 提供%ROWTYPE操作符, 返回一个记录类型, 其数据类型和数据库表的数据结构相一致。 使用%ROWTYPE特性的优点在于: 
    所引用的数据库中列的个数和数据类型可以不必知道; 
    所引用的数据库中列的个数和数据类型可以实时改变,容易保持一致,也不用修改PL/SQL程序。 
实例: 
Java代码 

 收藏代码

  1. declare  
  2.   v_empno  emp.empno%type  := &no;  
  3.   rec emp%rowtype;  
  4. begin  
  5.   select * into rec from emp where empno = v_empno;  
  6.   dbms_output.put_line(rec.ename);  
  7.   dbms_output.put_line(rec.job);  
  8.   dbms_output.put_line(rec.sal);  
  9.   dbms_output.put_line(rec.deptno);  
  10. end;  


待续************************************************************** 

第五种:更高级的数据类型psql表类型; 
   在前面我们已经讲解了varray,record类型,可以看出他们都是一维的。下面我们要讲解的是综合上面两种类型的数据类型plsql 表,该表示二维的。可以囊括上面的两种类型。 
   定义记录表(或索引表)数据类型。它与记录类型相似,但它是对记录类型的扩展。它可以处理多行记录,类似于高级中的二维数组,使得可以在PL/SQL中模仿数据库中的表。 

   
  

Python正确的学习路线,你一定不知道的薪资翻倍秘

如何从8K提至20K月薪,你要掌握学习那些技能

分享到:  

oracle10g 系统自带函数-subStr,sys_connec ... | oracle 视图(view)

评论

https://supanccy2013.iteye.com/blog/1997160

猜你喜欢

转载自blog.csdn.net/xuheng8600/article/details/85118850