Experiment 7 Database Programming

Level 1 defines a parameterless stored procedure named PROC_COUNT

Task Description
Define a named PROC_COUNTstored procedure without parameters, query the number of projects containing the word "factory" in the project name, and call the stored procedure.

Relevant knowledge
1. The engineering item table is composed Jof engineering item code ( JNO), engineering item name ( JNAME), and the city where the engineering item is located ( CITY).
JThe table is as shown in the figure below: the table
insert image description here
has been built J, and the structural information is as follows:
insert image description here
2. Stored procedure definition
The grammatical format of defining a stored procedure statement is as follows:

CREATE [OR REPLACE ] PROCEDURE <模式名.存储过程名> [WITH ENCRYPTION]
[(<参数名>  <参数模式>  <参数数据类型> [<默认值表达式>]
{
   
   ,<参数名>  <参数模式>  <参数数据类型> [<默认值表达式>] })] 
AS | IS
   [<说明语句端段>]
BEGIN
  <执行语句段>
  [Exception
    <异常处理语句段>]
END;

Among them:
(1) <pattern name.stored procedure name>: indicates the name of the created stored procedure.
(2) <parameter name>: indicates the name of the stored procedure parameter.
(3) WITH ENCRYPTION: It is optional. If you specify WITH ENCRYPTIONthe option , the statement block betweenBEGIN to is encrypted to prevent illegal users from viewing its specific content. The encrypted stored procedure or stored function definition can be queried in the system table. (4) <parameter mode>: Indicates the input/output mode of stored procedure parameters. The parameter mode can be set to or IN IN OUT IN OUT SQL DBA CREATE PROCEDURE DECLARE DECLARE declare BEGIN...END`, and before the statement, the syntax for defining variables is:ENDSYS.SYSTEXTS
INOUTIN OUT(OUT IN),默认为 类, 表示向存储过程传递参数,表示从存储过程返回参数。而表示传递参数和返回参数。 (5)<参数数据类型>:指明存储过程参数的数据类型。 (6)<说明语句端段>:由变量、游标和子程序等对象的申明构成。 (7)<执行语句段>:由语句和过程控制语句构成的执行代码。 (8)<异常处理语句段>:各种异常的处理程序,存储过程执行异常时调用,可默认。 注意:使用该语句的用户必须是或该存储过程的拥有者且具有数据库权限的用户;参数的数据类型只能指定变量类型,不能指定长度。 相关操作: ①定义变量 用于定义变量,在存储过程和函数中通过定义变量在

 DECLARE 变量名 变量类型 [DEFAULT 初始化值]

For example:

 DECLARE a, b INT DEFAULT 5;

SETDefine user variable
SETstatements can be used to assign values ​​to system variables or user variables. User variables mysqlare bound to the client. The set variables are only valid for the client used by the current user. The definition of user variables is as follows:

SET @var_name = expr [, @var_name = expr] ...

For example:

 SET @name = 'abc', @weight = 20;

③ Stored procedure call: CALLcommand procedure body call CALL sp_name[(parameter)];
View stored procedure: SHOW PROCEDURE STATUScommand ; command to check which stored procedures SHOW PROCEDURE STATUS where db='数据库名'exist in the corresponding database Used to modify certain characteristics of stored procedures. If you want to modify the content of the stored procedure, you can delete the original stored procedure first, and then create a new stored procedure with the same name; if you want to modify the name of the stored procedure, you can delete the original stored procedure first, and then create a new stored procedure with a different name process. ⑥ Stored procedure deletion: After the stored procedure is created, it will be kept on the database server until it is deleted. When there is an obsolete stored procedure in the database , we need to delete it from the database. Use the statement to delete the stored procedure that already exists in the database. The syntax format is as follows: The syntax description is as follows: • Procedure name: specify the name of the stored procedure to be deleted. : Specify this keyword to prevent errors caused by deleting non-existing stored procedures. Note: There is no parameter list or brackets after the stored procedure name. Before deleting, you must confirm that the stored procedure has no dependencies, otherwise other stored procedures associated with it will fail to run. ⑦Parameter output: @variable name Example: stored procedure ; the method of outputting parameters after calling is as follows: ; Note: it can be used with
ALTER PROCEDUREMySQLALTER PROCEDURE 存储过程名 [ 特征 ... ]ALTER PROCEDURE
DROP PROCEDURE命令MySQLMySQLDROP PROCEDURE
DROP PROCEDURE [ IF EXISTS ] <过程名>

IF EXISTS

OUTSELECTcreate procedure out_param(out p_out int)OUTCALL out_param(@pp_out); SELECT @pp_out
pp_outp_outThe same name
⑧ Modify the command terminator: DELIMITERCommand
insert image description here
⑨ Use SELECT …INTOa statement to assign a value to a variable
In MySQLa stored procedure, you can use SELECT …INTOa statement to assign a value to a variable. The statement queries the database and assigns the result to the variable. SELECT …INTOThe syntax of the statement is as follows:

 SELECT col_name[,...] INTO var_name[,...] table_expr

col_name: The column field name to be queried from the database;
var_name: The variable name, the column field name corresponds to the position in the column list and the variable list, and the value obtained by the query is assigned to the variable at the corresponding position; : The rest of the statement,
table_exprincluding SELECToptional clauses FROMand WHEREclauses. It should be noted that when using SELECT
the ... INTOstatement, the variable name cannot be the same as the field name in the data table, otherwise an error will occur. For example:

insert image description here

⑩ Return the variable value to the caller
The variable defined in the stored procedure, after a series of processing, the result value may need to be returned to the stored procedure caller. So how to return it? It is convenient to use SELECTstatements to return variables as result sets, for example:
insert image description here
create a stored procedure example: create a stored procedure to query the user name of a user ID in the T table
insert image description here

DELIMITER $
CREATE PROCEDURE PROC_COUNT()
BEGIN
SELECT COUNT(*) FROM J WHERE JNAME LIKE '%厂';
END ;$
DELIMITER ;
CALL PROC_COUNT();

Level 2 defines a parameterized stored procedure named PROC_JNAME

Task Description
Define a named PROC_JNAMEstored procedure with parameters to query and input the project name of any city.

DELIMITER $
CREATE PROCEDURE PROC_JNAME(IN TEMPNAME CHAR(10))
BEGIN
SELECT JNAME FROM J WHERE J.CITY=TEMPNAME;
END;$
DELIMITER ;

Level 3 defines a parameterized stored procedure named PROC_JINFO

Task description
Define a named PROC_JINFOstored procedure with parameters, query and input the project name, part name and total quantity of each part in any city, the results are first sorted by project name in ascending order, and then sorted by part name in ascending order.

DELIMITER $
CREATE PROCEDURE PROC_JINFO(IN TEMPNAME VARCHAR(10))
BEGIN
SELECT DISTINCT JNAME,PNAME,SUM(QTY) AS SUM_QTY
FROM SPJ
RIGHT JOIN J
ON J.JNO = SPJ.JNO
LEFT JOIN P
ON P.PNO = SPJ.PNO
WHERE CITY =  TEMPNAME
GROUP BY SPJ.JNO,SPJ.PNO
ORDER BY JNAME,PNAME;
END ;$
DELIMITER ;

Level 4 defines a parameterized stored procedure named PROC_AVGGRADE

Task Description
Define a named PROC_AVGGRADEstored procedure with parameters to count the average grade of any course.
Relevant knowledge
1. Curriculum schedule consists Courseof course number ( Cno), course name ( Cname), advanced course ( Cpno), and credit ( Ccredit).
CourseThe table is as shown in the figure below: the table
insert image description here
has been built , and the structural information is as follows: the code to create the table is:Course
insert image description here
CourseMysql

CREATE TABLE Course(
    Cno CHAR(4) PRIMARY KEY,
    Cname CHAR(9),
    Cpno CHAR(4),
    Ccredit INT
);

INSERT INTO Course VALUES('1','数据库','5',4);
INSERT INTO Course VALUES('2','离散数学','',2);
INSERT INTO Course VALUES('3','信息系统','1',4);
INSERT INTO Course VALUES('4','操作系统','6',3);
INSERT INTO Course VALUES('5','数据结构','7',4);
INSERT INTO Course VALUES('6','数据处理','',2);
INSERT INTO Course VALUES('7','PaSCal语言','6',4);

2. The student course selection form consists SCof student number ( Sno), course number ( Cno), and grade ( Grade).
SCThe table is as shown in the figure below: the table
insert image description here
has been built , and the structural information is as follows: the code to create the table is:SC
insert image description here
SCMysql

CREATE TABLE SC(
    Sno CHAR(9),
    Cno CHAR(4),
    Grade INT,
    PRIMARY KEY(Sno, Cno)
);

INSERT INTO SC VALUES('200215121','1',92);
INSERT INTO SC VALUES('200215121','2',85);
INSERT INTO SC VALUES('200215121','3',88);
INSERT INTO SC VALUES('200215122','2',90);
INSERT INTO SC VALUES('200215122','3',80);
INSERT INTO SC VALUES('200215123','1',60);
INSERT INTO SC VALUES('200215123','2',75);
INSERT INTO SC VALUES('200215124','1',52);
INSERT INTO SC VALUES('200215124','2',63);
INSERT INTO SC VALUES('200215125','1',100);
INSERT INTO SC VALUES('200215125','2',100);
INSERT INTO SC VALUES('200215126','2',34);
INSERT INTO SC VALUES('200215126','3',45);
INSERT INTO SC VALUES('200215127','2',86);
INSERT INTO SC VALUES('200215127','3',88);
DELIMITER $
CREATE PROCEDURE PROC_AVGGRADE(IN TEMPNAME VARCHAR(10))
BEGIN
SELECT AVG(Grade) AS AVG_Grade
FROM SC,Course
WHERE Cname = TEMPNAME 
AND Course.Cno=SC.Cno;
END;$
DELIMITER ;

Level 5 defines a parameterized stored procedure named PROC_SINFO

Task description
Define a named PROC_SINFOstored procedure with parameters, query and input the project name, part name and total quantity of each part supplied by any supplier, the results are first sorted by project name in ascending order, and then by part name in ascending order.

DELIMITER $
CREATE PROCEDURE PROC_SINFO(IN TEMPNAME VARCHAR(10))
BEGIN
SELECT DISTINCT JNAME,PNAME,SUM(QTY) AS SUM_QTY
FROM SPJ
RIGHT JOIN J
ON J.JNO = SPJ.JNO
RIGHT JOIN S
ON S.SNO = SPJ.SNO
LEFT JOIN P
ON P.PNO = SPJ.PNO
WHERE SNAME =  TEMPNAME
GROUP BY SPJ.JNO,SPJ.PNO
ORDER BY JNAME,PNAME;
END ;$
DELIMITER ;

Level 6 defines a parameterized stored procedure named PROC_JSEARCH

The task description
defines a stored procedure named PROC_JSEARCH with parameters. When a project code is input arbitrarily, it will return the name of the supplier (SNAME) and the name of the part (PNAME) that supplies the project part, and the name of the project (JNAME). The results are sorted first by supplier name in ascending order, then by part name in ascending order.

DELIMITER $
CREATE PROCEDURE PROC_JSEARCH(IN TEMPNAME CHAR(3))
BEGIN
SELECT DISTINCT SNAME,PNAME,JNAME
FROM SPJ
RIGHT JOIN J
ON J.JNO = SPJ.JNO
RIGHT JOIN S
ON S.SNO = SPJ.SNO
LEFT JOIN P
ON P.PNO = SPJ.PNO
WHERE J.JNO =  TEMPNAME
GROUP BY SPJ.JNO,SPJ.PNO
ORDER BY SNAME,PNAME;
END ;$
DELIMITER ;

Level 7 defines a parameterized stored procedure named PROC_SUPDATE

Task description
Define a named PROC_SUPDATEstored procedure with parameters, update the "supplier table" according to the value entered by the user S, and Schange the supplier name in the table to the input supplier name and the city name to the input according to the input supplier code the city name of .
Note that the input parameters of the stored procedure are Supplier Code, Supplier Name, City Name.

DELIMITER $
CREATE PROCEDURE PROC_SUPDATE(IN SNOTEMP CHAR(3),IN SNAMETEMP CHAR(10),IN CITYTEMP CHAR(10))
BEGIN
UPDATE S
SET SNO = SNOTEMP,SNAME = SNAMETEMP,CITY = CITYTEMP
WHERE SNO = SNOTEMP;
END;$
DELIMITER ;

Level 8 defines a parameterized stored procedure named PROC_AVGWEIGHT

Task description
Define a named PROC_AVGWEIGHTstored procedure with parameters, which is required to obtain the "average weight of all parts corresponding to the project input by the user input" provided by the "supplier input by the user", and return the average weight result through the output variable AVG_WEIGHT, According to the input Supplier is S2, Project is J1, execute the above stored procedure. (Note to take into account the number of parts)

DELIMITER $
CREATE PROCEDURE PROC_AVGWEIGHT(IN SNOTEMP CHAR(3),IN JNOTEMP CHAR(3),OUT TEMP INT(11))
BEGIN 
SELECT AVG(WEIGHT) INTO TEMP
FROM P
WHERE P.PNO IN(
SELECT DISTINCT PNO
FROM SPJ 
WHERE SNO=SNOTEMP 
AND JNO=JNOTEMP
);
END;$
DELIMITER ;
CALL PROC_AVGWEIGHT('S2','J1',@AVG_WEIGHT);
SELECT @AVG_WEIGHT;

Level 9 defines a parameterized stored procedure named PROC_JGRADE

Task description
Define a named PROC_JGRADEstored procedure with parameters, view JNOthe distribution of the number of parts used in a certain project, and make statistics in segments according to Sfile <1000, Mfile 1000-2000, Lfile >2000, input a project JNO, and output the project number JNOgrade JTYPE.

DELIMITER $
CREATE PROCEDURE PROC_JGRADE(IN JNOTEMP VARCHAR(10),OUT TEMP VARCHAR(10))
BEGIN
DECLARE NUMBER INT(4);
SELECT SUM(QTY) INTO NUMBER
FROM SPJ
WHERE JNO =JNOTEMP;
IF (NUMBER < 1000)
THEN SET TEMP ='S';
ELSEIF (NUMBER > 1000 & NUMBER < 2000)
THEN SET TEMP ='M';
ELSEIF (NUMBER > 2000)
THEN SET TEMP ='L';
END IF;
END;$
DELIMITER ;

Level 10 defines a stored procedure named PROC_UPDATEGRADE

Task description
Define a PROC_UPDATEGRADEstored procedure named to change the grades of students' course selection from the percentile system to the grade system (ie A, B, C, D, E), where A[90,100], B[80,90), C[70,80), D[ 60,70), Eis [0,60).

DELIMITER $
CREATE PROCEDURE PROC_UPDATEGRADE()
BEGIN
UPDATE SC SET rank='A' WHERE Grade >=90 AND Grade<=100; 
UPDATE SC SET rank='B' WHERE Grade >=80 AND Grade<90; 
UPDATE SC SET rank='C' WHERE Grade >=70 AND Grade<80;  
UPDATE SC SET rank='D' WHERE Grade >=60 AND Grade<70;
UPDATE SC SET rank='E' WHERE Grade >=0 AND Grade<60;  
END;$
DELIMITER ;
CALL PROC_UPDATEGRADE();

Level 11 uses a cursor to define a stored procedure named PROC_JGRADE without parameters

Task description
Use a cursor to define a PROC_JGRADEstored procedure called no parameter, and count the distribution of the number of parts used in each project. According to Sthe file <1000, Mfile 1000-2000, and Lfile>2000, the statistics are segmented, that is, the output project JNOand the number of parts used in each project number, the number of parts used in each project, and the results are JNOsorted in ascending order of the project number.
Relevant knowledge
1. The supply situation table is composed SPJof supplier code ( SNO), part code ( PNO), project code ( JNO), and supply quantity ( QTY), indicating the quantity of a certain part supplied by a certain supplier to a certain project QTY.
SPJThe table is as shown in the figure below:
insert image description here
the SPJ table has been built, and the structural information is as follows:
insert image description here
2. Cursor
Operation of the cursor
① Definition of the cursor

DECLARE 光标名称 CURSOR FOR 查询语法

example:

DECLARE CURSOR_NAME CURSOR FOR SELECT_STATEMENT;

② Open the cursor

 OPEN 光标名称

example:

OPEN CURSOR_NAME;

③ Get the data in the cursor

FETCH 光标名称 INFO VAR_NAME [,VAR_NAME ].....

example:

FETCH CURSOR_NAME INFO VAR_NAME;

④ Close the cursor

 CLOSE 光标名称

example:

 CLOSE CURSO_NAME;

⑤ Release the cursor

DEALLOCATE 光标名称

example:

DEALLOCATE CURSOR_NAME;

Example of using a cursor: Create a stored procedure using a cursor STUDENTto count the number of records whose age is greater than 19 in the table. STUDENTThe code for creating the table is as follows:

 CREATE TABLE STUDENT(
    STUID INT PRIMARY KEY AUTO_INCREMENT,
    STUNAME VARCHAR(20),
    STUSEX VARCHAR(2),
    STUAGE INT
);
INSERT INTO STUDENT(STUNAME,STUSEX,STUAGE) VALUES
('小明','男',20),
('小花','女',19),
('大赤','男',20),
('可乐','男',19),
('莹莹','女',19);

Create a stored procedure code using a cursor:

DELIMITER $
CREATE PROCEDURE PROC_STAT()
BEGIN
    # 创建 用于接收游标值的变量
    DECLARE ID,AGE,TOTAL INT;
    DECLARE NAME,SEX CHAR(10);
    # 游标结束的标志
    DECLARE DONE INT DEFAULT 0;
    # 声明游标
    DECLARE CUR CURSOR FOR SELECT STUID,STUNAME,STUSEX,STUAGE FROM STUDENT WHERE STUAGE > 19;
    # 指定游标循环结束时的返回值 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET DONE = 1;
    # 打开游标
    OPEN CUR;
    # 初始化变量
    SET TOTAL = 0;
    # WHILE 循环
    WHILE DONE != 1 DO
        FETCH CUR INTO ID,NAME,SEX,AGE;
          IF DONE != 1 THEN
             SET TOTAL = TOTAL + 1;
          END IF;    
    END WHILE;
    # 关闭游标
    CLOSE CUR;
    # 输出累计的结果
    SELECT TOTAL;
END$
DELIMITER ;
# 调用存储过程
CALL PROC_STAT();
DELIMITER $
CREATE PROCEDURE PROC_JGRADE()
BEGIN
DECLARE PTYPE CHAR(10);
DECLARE NJNO CHAR(10);
DECLARE JNOTEMP INT DEFAULT 0;
DECLARE QTYTEMP INT DEFAULT 0;
DECLARE TEMP CURSOR FOR SELECT JNO,SUM(QTY)

FROM SPJ 
GROUP BY JNO 
ORDER BY JNO;
OPEN TEMP;
CREATE TEMPORARY TABLE temp_table(
SJNO CHAR(10) NOT NULL,
SQTY CHAR(10) NOT NULL,
JTYPE CHAR(10) NOT NULL
);
REPEAT 
FETCH TEMP INTO NJNO,QTYTEMP;
IF(QTYTEMP<1000)
THEN
SET PTYPE='S';
ELSEIF(1000<=QTYTEMP AND QTYTEMP <=2000)
THEN
SET PTYPE='M';
ELSEIF(QTYTEMP>2000)
THEN
SET PTYPE='L';
END IF;
INSERT INTO temp_table VALUES(NJNO,QTYTEMP,PTYPE);
SET JNOTEMP =JNOTEMP+1;
UNTIL JNOTEMP>=5
END REPEAT;
CLOSE TEMP;
SELECT*FROM temp_table;
END;$
DELIMITER ;

Level 12 uses a cursor to define a stored procedure named PROC_NUMGRADE with parameters

Task description
Use a cursor to define a PROC_NUMGRADEstored procedure named with parameters to count the grade distribution of a course, that is, to count the number of people according to each score segment, according to 100, [90,100), [80,90), [70,80), [60 ,70), [0,60) segment statistics.
Relevant knowledge
1. Curriculum schedule consists Courseof course number ( Cno), course name ( Cname), advanced course ( Cpno), and credit ( Ccredit).
CourseThe table is as shown in the figure below:
insert image description here
the Course table has been built, and the structural information is as follows: the code
insert image description here
to create Coursethe table Mysqlis:

CREATE TABLE Course(
    Cno CHAR(4) PRIMARY KEY,
    Cname CHAR(9),
    Cpno CHAR(4),
    Ccredit INT
);

INSERT INTO Course VALUES('1','数据库','5',4);
INSERT INTO Course VALUES('2','离散数学','',2);
INSERT INTO Course VALUES('3','信息系统','1',4);
INSERT INTO Course VALUES('4','操作系统','6',3);
INSERT INTO Course VALUES('5','数据结构','7',4);
INSERT INTO Course VALUES('6','数据处理','',2);
INSERT INTO Course VALUES('7','PaSCal语言','6',4);

2. The student course selection form consists SCof student number ( Sno), course number ( Cno), and grade ( Grade).
SCThe table is as shown in the figure below: the table
insert image description here
has been built , and the structural information is as follows: the code to create the table is:SC
insert image description here
SCMysql

CREATE TABLE SC(
    Sno CHAR(9),
    Cno CHAR(4),
    Grade INT,
    PRIMARY KEY(Sno, Cno)
);

INSERT INTO SC VALUES('200215121','1',92);
INSERT INTO SC VALUES('200215121','2',85);
INSERT INTO SC VALUES('200215121','3',88);
INSERT INTO SC VALUES('200215122','2',90);
INSERT INTO SC VALUES('200215122','3',80);
INSERT INTO SC VALUES('200215123','1',60);
INSERT INTO SC VALUES('200215123','2',75);
INSERT INTO SC VALUES('200215124','1',52);
INSERT INTO SC VALUES('200215124','2',63);
INSERT INTO SC VALUES('200215125','1',100);
INSERT INTO SC VALUES('200215125','2',100);
INSERT INTO SC VALUES('200215126','2',34);
INSERT INTO SC VALUES('200215126','3',45);
INSERT INTO SC VALUES('200215127','2',86);
INSERT INTO SC VALUES('200215127','3',88);
DELIMITER $
CREATE PROCEDURE PROC_NUMGRADE(IN TEMPCNAME CHAR(9))
BEGIN
    # 创建 用于接收游标值的变量
    DECLARE TEMPGRADE INT;
    # 游标结束的标志
    DECLARE DONE INT DEFAULT 0;
    # 声明游标
    DECLARE CUR CURSOR FOR
    SELECT Grade
    FROM Course,SC
    WHERE Cname = TEMPCNAME AND Course.Cno = SC.Cno;
    # 指定游标循环结束时的返回值 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET DONE = 1;
    # 创建临时表
    CREATE TEMPORARY TABLE TMEP_TABLE(
        Type CHAR(30),
        Num INT
    );
    INSERT INTO TMEP_TABLE VALUES('100', 0);
    INSERT INTO TMEP_TABLE VALUES('[90,100)', 0);
    INSERT INTO TMEP_TABLE VALUES('[80,90)', 0);
    INSERT INTO TMEP_TABLE VALUES('[70,80)', 0);
    INSERT INTO TMEP_TABLE VALUES('[60,70)', 0);
    INSERT INTO TMEP_TABLE VALUES('[0,60)', 0);
    # 打开游标
    OPEN CUR;
    # WHILE 循环
    WHILE DONE != 1 DO
        FETCH CUR INTO TEMPGRADE;
        IF DONE != 1 THEN
            IF TEMPGRADE=100 THEN UPDATE TMEP_TABLE SET Num=Num+1 WHERE Type='100';
            ELSEIF TEMPGRADE>=90 AND TEMPGRADE<100 THEN UPDATE TMEP_TABLE SET Num=Num+1 WHERE Type='[90,100)';
            ELSEIF TEMPGRADE>=80 AND TEMPGRADE<90 THEN UPDATE TMEP_TABLE SET Num=Num+1 WHERE Type='[80,90)';
            ELSEIF TEMPGRADE>=70 AND TEMPGRADE<80 THEN UPDATE TMEP_TABLE SET Num=Num+1 WHERE Type='[70,80)';
            ELSEIF TEMPGRADE>=60 AND TEMPGRADE<70 THEN UPDATE TMEP_TABLE SET Num=Num+1 WHERE Type='[60,70)';
            ELSEIF TEMPGRADE>=0 AND TEMPGRADE<60 THEN UPDATE TMEP_TABLE SET Num=Num+1 WHERE Type='[0,60)';
            END IF;  
        END IF; 
    END WHILE;
    # 关闭游标
    CLOSE CUR;
    # 输出累计的结果
    SELECT * FROM TMEP_TABLE;
END;$
DELIMITER ;

Guess you like

Origin blog.csdn.net/weixin_51571728/article/details/128133977