基础训练

mysql存储过程：
先看完基础训练，再看代码，就容易了。
基础训练1：按指定词语分割句子
CREATE DEFINER=`root`@`localhost` PROCEDURE `z1`()
BEGIN
declare say varchar(100);#全句
declare start1char varchar(10);#句子开始的1个字符
declare start2char varchar(10);#句子开始的2个字符
declare last1char varchar(10);#句子最后的1个字符
declare last2char varchar(10);#句子最后的2个字符
declare word varchar(10);#分割词
declare position_start varchar(10);#分割词在句中的开始位置
declare word_length varchar(10);#分割词的长度
declare position_last varchar(10);#分割词在句中的结束位置
declare word_left varchar(10);#分割词左边句
declare word_right varchar(10);#分割词右边句
declare word_left1 varchar(3);#分割词左边1个字符
declare word_left2 varchar(6);#分割词左边2个字符
declare word_right1 varchar(3);#分割词右边1个字符
declare word_right2 varchar(6);#分割词右边2个字符
declare word2 varchar(10);#第2个分割词
declare word2_left varchar(50);#第2个分割词的左边句
declare word1_word2 varchar(50);#两个分割词之间的内容
declare word_con varchar(10);#词语连接
set say = "abcdefg";
/*
函数说明：substring(全句,截取的位置,截取的长度)
截取的位置：正数从左数，负数从右数
截取的长度：从左向右数
*/
set start1char = substring(say,1,1);#句子开始的1个字符
set start2char = substring(say,1,2);#句子开始的2个字符
set last1char = substring(say,-1,1);#句子最后的1个字符
set last2char = substring(say,-2,2);#句子最后的2个字符
-- 分割词
set word = "d";
-- 按分割词截取
set position_start = instr(say,word);#分割词在句中的开始位置
set word_length = char_length(word);#分割词的长度
set position_last = position_start + word_length;#分割词在句中的结束位置
set word_left = substring(say,1,position_start-1);#分割词左边句
set word_right = substring(say,position_last);#分割词右边句
-- 合并，这样仅需要全句和分割词两个参数
set word_left = substring(say,1,instr(say,word)-1);#分割词左边句
-- 或word_left = substring_index(say,word,1);
set word_right = substring(say,instr(say,word) + char_length(word));#分割词右边句
-- 或word_right = substring_index(say,word,-1);
-- 单个字符
set word_left1 = "";
set word_left1 = substring(word_left,-1,1);#分割词左边1个字符
set word_left2 = substring(word_left,-2,2);#分割词左边2个字符
set word_right1 = substring(word_right,1,1);#分割词右边1个字符
set word_right2 = substring(word_right,1,2);#分割词右边2个字符
-- 展开，这样仅需要全句和分割词两个参数
set word_left1 = substring(substring(say,1,instr(say,word)-1),-1,1);
set word_left2 = substring(substring(say,1,instr(say,word)-1),-2,2);
set word_right1 = substring(substring(say,instr(say,word) + char_length(word)),1,1);
set word_right2 = substring(substring(say,instr(say,word) + char_length(word)),1,2);
-- 再添加一个分割词word2
set word2 = "f";
/*
函数说明：substring_index(全句,截取的字符,第几次出现该字符)
这个函数是以第n次出现的分割词，从右向左截取句子，如果要从左向右，第三个参数填负数
*/
set word2_left = substring_index(say,word2,1);
-- 两个分割词之间的内容：substring_index(substring_index(全句,分割词1,-1),分割词2,1)
set word1_word2 = substring_index(substring_index(say,word,-1),word2,1);
-- 词语连接
-- 函数说明：concat(要连接起来的各个词语)
set word_con = concat(word,"-",word2);
-- 显示结果
select say,start1char,start2char,last1char,last2char,
word,position_start,word_length,position_last,word_left,word_right,
word_left1,word_left2,word_right1,word_right2,
word2,word2_left,word1_word2,word_con;
/*显示的值：
say:abcdefg
start1char：a
start2char：ab
last1char：g
last2char：fg
word：d
position_start：4
word_length：1
position_last：5
word_left：abc
word_right：efg
word_left1：c
word_left2：bc
word_right1：e
word_right2：ef
word2：f
word2_left：abcde
word1_word2：e
word_con：d-f
*/
END

基础训练2：游标的使用
这个存储过程用于找到句子中包含的名词。
在主谓宾句型中，先找动词，后找名词。动词左边的名词就是主语，动词右边的名词就是宾语。这样，主谓宾都找到了。
CREATE DEFINER=`root`@`localhost` PROCEDURE `z2`()
BEGIN
/*
游标可以一行行逐个从词语表中读取词语
游标有个缺点，符合条件的最后一行，会重复读2次，为了防止重复，可以加个old_word变量
判断句子是否有属于名词表的词，既名词表的词是否出现在句子里
*/
declare say varchar(100);#输入的话语
declare find_word varchar(10);#要找的词
declare old_word varchar(10);#游标上次读取的值
declare basket varchar(10);#临时变量，用于存储游标读出的数据
declare sign int default 0;#游标变量，0表示没有没有读取到底，1表示读取到底
declare noun_fish cursor for select word_col from noun;#游标：找名词
declare continue handler for not found set sign = 1;#继续读取，读取到底，使sign值变1
set say = "可爱的兔子蹦蹦跳跳";#前提是名词表里已经有名词“兔子”
set find_word = "";#要找的词，默认为空。如果经过游标程序，依然为空，就说明没找到
set sign = 0;#0表示游标还没有读到底
set old_word= "";#初始化为空
set basket = "";#游标从表格中，每次读出的一个数据，放入临时变量basket中
open noun_fish;#打开游标
while sign != 1 do#开始循环，游标还没有读到底（sign为0，不为1），就一直循环操作
fetch noun_fish into basket;#把读到的值放入临时变量basket中
-- 函数说明：instr(全句,词语)，如果词语包含在句子中，返回值大于0（词语在句子中的位次），否则返回值等于0
if instr(say,basket) > 0 and basket != old_word then#游标这次从表格中读出的词语，包含在句子中，且与上次读到的值不一样
set find_word = basket;#找到了，就赋值
set old_word = basket;#现在找到的新词，对下一次循环来说，就是旧词了
end if;
end while;#结束循环
close noun_fish;#关闭游标
-- 显示结果
select find_word;
/*显示的值
find_word：兔子
*/
END

基础训练3：动态SQL
CREATE DEFINER=`root`@`localhost` PROCEDURE `z3`()
BEGIN
-- 动态SQL就是把一堆SQL指令片段拼接在一起去执行，可以让SQL灵活的用啥拼啥
declare n1 varchar(100);
declare n2 varchar(100);
declare n3 varchar(100);
declare n4 varchar(100);
declare n5 varchar(100);
declare n varchar(500);
declare result varchar(10);
set n1 = "select object_col ";
set n2 = "from know ";
set n3 = "where subject_col = ";
set n4 = "'猫'";#双引号里要加单引号
set n5 = " limit 1";#只要一个结果
set result = "";#默认为空值
set n = concat(n1,n2,n3,n4,n5);#p1的值为select subject_col from know where subject_col = '猫' limit1
set @p1 = n;
prepare p2 FROM @p1;#准备
execute p2 ;#执行查询
deallocate prepare p2;#释放
set result = @p3;#获取结果
-- 显示结果
select result;
-- 显示的值
-- result：鼠
END

基础训练4：select时，常用的函数
CREATE DEFINER=`root`@`localhost` PROCEDURE `z4`()
BEGIN
/*
select的对象的值，不能是NULL值。如果是NULL值，就会报错，所以要用IFNULL函数
所以select subject_col from know的安全写法（避免报错的写法）是select IFNULL(subject_col,'') from know
IFNULL(列名,'')意思是把列中找到的NULL值，变为空值，这样就避报错
空值和NULL的区别:
计算行数count(列名)时，如果遇到NULL就忽略不计（跳过），但是遇到空值要记为一行数据
存储上，空值不占存储空间，而NULL值要占存储空间
可见，空值表示没有，NULL表示忽略
空值用 = 和 != 比较，而NULL值只能用is和is not比较
group_concat函数：select时，把同一列的多个查询结果，连接到一起，成为一个聚合值
concat_ws函数；select时，把同一行上，不同列的查询结果，连接到一起，成为一个聚合值
concat_ws('分隔符',列名1,列名2)
如果输入set result = (select object_col from know where subject_col = '猫');会报错，因为返回值不止一个
解决方法1：用group_concat函数，把所有返回值，拼接成一个返回值
解决方法2：写limit 1 就只返回第一个返回值
*/
declare result varchar(100);
declare result2 varchar(100);
declare result3 varchar(100);
set result = (select group_concat(object_col) from know where subject_col = '猫');
-- 再复杂一点：返回值不要有重复值，用distinct
set result2 = (select group_concat(distinct object_col) from know where subject_col = '猫');
-- 把三列的结果，以逗号连接到一起，作为一个结果返回
set result3 = (select concat_ws(',',verb1_col,verb2_col,object_col) from know where subject_col = '猫' limit 1);
-- 显示结果
select result,result2,result3;#把多个返回值拼接到一起返回
END

基础训练5：最简单句型的语义理解和问答
CREATE DEFINER=`root`@`localhost` PROCEDURE `z5`()
BEGIN
-- 基础训练：最基本的语义理解和问答
-- 定义变量
declare say varchar(100);#输入的话语
declare subject_find varchar(10);#主语
declare verb_find varchar(10);#谓语动词
declare object_find varchar(10);#宾语
declare left_part varchar(100);#谓语动词左边的句子
declare right_part varchar(100);#谓语动词右边的句子
declare sl_col varchar(100);#动态sql中，select要查询的列的内容
declare wr varchar(100);#动态sql中，select的条件where的内容
declare s1 varchar(500);#动态sql中，临时变量，s1内容量大
declare judge varchar(10);#判断要插入表的数据，表中是否已存在
declare reply varchar(100);#电脑的回答（回答语义理解）
declare answer varchar(100);#电脑的回答（普通方式问答）
declare answer2 varchar(100);#电脑的回答（动态sql方式问答）
-- 定义游标
declare old_word varchar(10);#游标上次读取的值
declare basket varchar(10);#临时变量，用于存储游标读出的数据
declare sign int default 0;#游标变量，0表示没有没有读取到底，1表示读取到底
declare noun_fish cursor for select word_col from noun;#游标：找名词（从名词表noun的word_col列寻找）
declare verb_fish cursor for select word_col from verb;#游标：找动词（从动词表verb的word_col列寻找）
declare continue handler for not found set sign = 1;#继续读取，读取到底，使sign值变1
-- 变量初始化
set say = "白色的猫吃黑色的鼠";#输入的语句
set subject_find = "";#主语初始化为空值
set verb_find = "";#谓语动词初始化为空值
set object_find = "";#宾语初始化为空值
set left_part = "";#谓语动词左边句初始化为空值
set right_part = "";#谓语动词右边句初始化为空值
set sl_col = "";#select对象内容初始化为空值
set wr = "";#条件where的内容初始化为空值
set s1 = "";#动态sql的临时变量初始化为空值
set judge = "";#judge变量用于判断要插入表中的数据是否已存在于表中，初始化为空
set reply = "";#电脑的回答（语义理解）初始化为空值
set answer = "";#电脑的回答（普通方式问答）初始化为空值
set answer2 = "";#电脑的回答（动态sql方式问答）初始化为空值
-- 第一步：找谓语动词
set sign = 0;#0表示游标还没有读到底
set old_word= "";#初始化为空
set basket = "";#游标从表格中，每次读出的一个数据，放入临时变量basket中
open verb_fish;#打开找动词的游标
while sign != 1 do#开始循环，游标还没有读到底（sign为0，不为1），就一直循环操作
fetch verb_fish into basket;#把读到的值放入临时变量basket中
-- 函数说明：instr(全句,词语)，如果词语包含在句子中，返回值大于0（词语在句子中的位次），否则返回值等于0
if instr(say,basket) > 0 and basket != old_word then#游标这次从表格中读出的词语，包含在句子中，且与上次读到的值不一样
set verb_find = basket;#找到了词语“吃”，就赋值给动词
set old_word = basket;#现在找到的新词，对下一次循环来说，就是旧词了
end if;
end while;#结束循环
close verb_fish;#关闭游标
/*
第二步：按谓语动词分割句子
谓语动词左边句找到的名词，就是主语
谓语动词右边句找到的名词，就是宾语
*/
if verb_find != "" then#找到了谓语动词“吃”
-- 按分割词分割句子的方法，前面存储过程z1已经讲过了
set left_part = substring(say,1,instr(say,verb_find)-1);#分割词（谓语动词“吃”）的左边句，既“白色的猫”
set right_part = substring(say,instr(say,verb_find) + char_length(verb_find));#分割词（谓语动词“吃”）的右边句，既“黑色的鼠”
-- 第三步：谓语动词左边句（left_part）中，找主语
set sign = 0;#0表示游标还没有读到底
set old_word= "";#初始化为空
set basket = "";#游标从表格中，每次读出的一个数据，放入临时变量basket中
open noun_fish;#打开找名词的游标
while sign != 1 do#开始循环，游标还没有读到底（sign为0，不为1），就一直循环操作
fetch noun_fish into basket;#把读到的值放入临时变量basket中
-- 函数说明：instr(全句,词语)，如果词语包含在句子中，返回值大于0（词语在句子中的位次），否则返回值等于0
if instr(left_part,basket) > 0 and basket != old_word then#游标这次从表格中读出的词语，包含在句子中，且与上次读到的值不一样
set subject_find = basket;#找到了词语“猫”，就赋值给主语
set old_word = basket;#现在找到的新词，对下一次循环来说，就是旧词了
end if;
end while;#结束循环
close noun_fish;#关闭游标
-- 第四步：谓语动词右边句（right_part）中，找宾语
set sign = 0;#0表示游标还没有读到底
set old_word= "";#初始化为空
set basket = "";#游标从表格中，每次读出的一个数据，放入临时变量basket中
open noun_fish;#打开找名词的游标
while sign != 1 do#开始循环，游标还没有读到底（sign为0，不为1），就一直循环操作
fetch noun_fish into basket;#把读到的值放入临时变量basket中
-- 函数说明：instr(全句,词语)，如果词语包含在句子中，返回值大于0（词语在句子中的位次），否则返回值等于0
if instr(right_part,basket) > 0 and basket != old_word then#游标这次从表格中读出的词语，包含在句子中，且与上次读到的值不一样
set object_find = basket;#找到了词语“鼠”，就赋值给宾语
set old_word = basket;#现在找到的新词，对下一次循环来说，就是旧词了
end if;
end while;#结束循环
close noun_fish;#关闭游标
-- 第五步：把找到的主谓宾，插入到知识表中
-- 如果要插入表中的数据，已存在于表中，judge就会被赋值，否则judge的值为空值或NULL值
-- limit 1表示只要一个返回结果，如果返回多个结果就会报错
set judge = (select subject_col from know where verb1_col = verb_find and object_col = object_find limit 1);
-- 如果表中没有此数据，才插入
if judge = "" or judge is NULL then
insert into know(subject_col,verb1_col,object_col) values(subject_find,verb_find,object_find);
end if;
-- 第六步：电脑的回答（语义理解）
set reply = concat("主语：",subject_find," 谓语动词：",verb_find," 宾语：",object_find);#concat函数用于连接字符串和变量
else#没有找到谓语动词
set reply = "没有谓语动词";#电脑的回答
end if;
/*
第七步（方式一）：普通方式问答
用户输入“什么吃鼠”
按前面的方法，找到主语是词语“什么”，谓语动词是词语“吃”，宾语是词语“鼠”
前提是词语“什么”，已经作为名词，存入名词表中
提问主语，那么select的对象就是主语，where条件的对象是谓语动词“吃”和宾语“鼠”
*/
set say = "什么吃鼠";
-- 找寻方法（语义理解）就不重复写了，按前面的方法就行，下面只写找寻结果
set subject_find = "什么";#主语为“什么”
set verb_find = "吃";#谓语动词为“吃”
set object_find = "鼠";#宾语为“鼠”
if subject_find = "什么" then#提问主语，如果用户输入“什么吃鼠”，那么此时subject_find就为“什么”
-- 知识表中，主语作为select的对象，谓语动词和宾语作为select的条件（where条件）
set answer = (select subject_col from know where verb1_col = verb_find and object_col = object_find limit 1);
elseif verb_find = "怎么" then#提问谓语动词，如果用户输入“猫怎么鼠”，那么此时verb_find就为“怎么”
-- 知识表中，谓语动词作为select的对象，主语和宾语作为select的条件（where条件）
set answer = (select verb1_col from know where subject_col = subject_find and object_col = object_find limit 1);
elseif object_find = "什么" then#提问宾语，如果用户输入“猫吃什么”，那么此时object_find就为“什么”
-- 知识表中，宾语作为select的对象，主语和谓语动词作为select的条件（where条件）
set answer = (select object_col from know where subject_col = subject_find and verb1_col = verb_find limit 1);
end if;
/*
第七步（方式二）：动态sql方式问答
动态sql方式，就是根据需要，组装sql指令碎片，形成查询程序，然后执行
*/
if subject_find = "什么" then
set sl_col = "subject_col";#select的对象列为subject_col
/*
如果subject_col为没有值，就会返回NULL值，就会报错
所以最好把select subject_col写为select IFNULL(subject_col,'')
这样的话，如果subject_col为NULL值时，就会替换成空值，而防止报错
因此最好写为sl_col = "IFNULL(subject_col,'')";
注意：双引号"里要包单引号'，双引号里不能包双引号，单引号里又可以包双引号
concat函数连接字符串和变量，逗号把各部分连接在一起
变量verb_find左右，都要有单引号'包着，因为执行时，verb_find的内容是'吃'
这里的concat把8个部分连接在一起，逗号是各部分的分隔符
第1个部分：verb1_col =
第2个部分：'
第3个部分：verb_find
第4个部分：'
第5个部分：and object_col =
第6个部分：'
第7个部分：object_find
第8个部分：'
这8部分执行时为verb1_col = '吃' and object_col = '鼠'
这8部分就是select语句中，条件where的内容
执行时，变量verb_find已经替换为吃，变量object_find已经替换为鼠
*/
set wr = concat("verb1_col = ","'",verb_find,"'"," and object_col = ","'",object_find,"'");
elseif verb_find = "怎么" then#如果提问词是谓语“怎么”，例如猫怎么鼠。可见不同的提问词，产生的动态sql指令不同
set sl_col = "verb1_col"#select的对象列为谓语动词列，既verb1_col
set wr = concat("subject_col = ","'",subject_find,"'"," and object_col = ","'",object_find,"'");
elseif object_find = "什么" then#如果提问词是宾语“什么”，例如猫吃什么
set sl_col = "object_col";#select的对象列为宾语列，既object_col
set wr = concat("subject_col = ","'",subject_find,"'"," and verb1_col = ","'",verb_find,"'");
end if;
/*
sql碎片组装成sql指令
变量sl_col存放的是select的对象列
变量wr存放的是where的条件内容
group_concat函数是吧多个返回值拼接成一个返回值
separator '、'表示这些返回值用顿号（、）隔开
distinct表示不要重复的值，保证每个值都不一样
into @find 是吧select的查询结果，放到变量find里
from know 是从知识表know里查询
动态sql的语法规则是有些古怪
先要prepare准备，然后execute执行，最后deallocate prepare释放变量
有些变量必须加@，有些变量不用加@
s1要定义，带@号的不用定义，prepare对象（s3）也不用定义
*/
set s1 = concat("select group_concat(distinct ",sl_col," separator '、') into @find from know where ",wr);
set @s2 = s1;
prepare s3 FROM @s2;#准备
execute s3 ;#执行查询
deallocate prepare s3;#释放
set answer2 = @find;#获取结果
-- 第八步：输出结果
select reply,answer,answer2;
/*
显示：
reply：主语：猫 谓语动词：吃 宾语：鼠
answer：猫
answer2：猫
*/
END

基础训练6：按标点符号分割段落
CREATE DEFINER=`root`@`localhost` PROCEDURE `z6`()
BEGIN
-- 基础训练：按标点符号（逗号、句号、问号、感叹号）分割句子
declare say varchar(300);#输入的话语
declare sp1 varchar(100);#第一次循环，按逗号分割句子，所得到的分割句。sp是split的简写
declare sp2 varchar(100);#第二次循环，按句号分割句子，所得到的分割句
declare sp3 varchar(100);#第三次循环，按问号分割句子，所得到的分割句
declare sp4 varchar(100);#第四次循环，按感叹号分割句子，所得到的分割句
declare i1 int;#第一次循环的循环变量
declare i2 int;#第二次循环的循环变量
declare i3 int;#第三次循环的循环变量
declare i4 int;#第四次循环的循环变量
declare sentence varchar(100);#符号（。，？！）分成的每个单句
declare n1 varchar(100);#第1个单句
declare n2 varchar(100);#第2个单句
declare n3 varchar(100);#第3个单句
declare n4 varchar(100);#第4个单句
declare n5 varchar(100);#第5个单句
declare n6 varchar(100);#第6个单句
-- 初始化
set say = "猫在院子里玩，看见了一只鼠。竟然有老鼠！猫走过去，猫会吃它吗？肯定会的。";
set n1 = "";
set n2 = "";
set n3 = "";
set n4 = "";
set n5 = "";
set n6 = "";
/*
第一次循环：先按逗号分割句子。因为段落中，逗号最多，能分出最多的段
第二次循环：逗号分割出的分割段中，再用句号进一步分割
第三次循环：句号分割出的分割段中，再用问号进一步分割
第四次循环：问号分割出的分割段中，再用感叹号进一步分割
四次循环下来，就是单句了，存放于sp4中
*/
-- 第一层循环：按逗号分割句子，循环次数取决于逗号把句子分成的段落数
set sp1 = "";
set i1 = 0;
/*
长度1：char_length(say)::既原本句子的长度
长度2：char_length(REPLACE(say,'，',''))，既逗号被变空（去掉逗号）后，句子的长度
replace是替换函数，把say中的逗号，替换为空无
那么长度1减长度2，就是逗号数量。之后再加1，就是逗号把句子分割成的段落数。为什么要加1？因为2个逗号分割出3段，而不是2段
当循环变量i1小于逗号把句子分成的段落数，就执行循环，循环结束后，i1会增加1，直到每个段落都被处理完，既i1增加到与段落数量值相等
*/
while i1 < char_length(say)-char_length(REPLACE(say,'，',''))+1 do
set sp1 = SUBSTRING_INDEX(SUBSTRING_INDEX(say,'，',i1+1),'，',-1);#sp：split，逗号把句子分成的段
-- 第二循环：按句号分割句子
set sp2 = "";
set i2 = 0;
while i2 < char_length(sp1)-char_length(REPLACE(sp1,'。',''))+1 do
set sp2 = SUBSTRING_INDEX(SUBSTRING_INDEX(sp1,'。',i2+1),'。',-1);
-- 第三层循环：按问号分割句子
set sp3 = "";
set i3 = 0;
while i3 < char_length(sp2)-char_length(REPLACE(sp2,'？',''))+1 do
set sp3 = SUBSTRING_INDEX(SUBSTRING_INDEX(sp2,'？',i3+1),'？',-1);
-- 第四层循环：按感叹号分割句子
set sp4 = "";
set i4 = 0;
while i4 < char_length(sp3)-char_length(REPLACE(sp3,'！',''))+1 do
set sp4 = SUBSTRING_INDEX(SUBSTRING_INDEX(sp3,'！',i4+1),'！',-1);
-- 现在，按符号分割句子完成，已都是符号分出的单句sp4
-- 给每个单句赋值
if n1 = "" then
set n1 = sp4;
elseif n2 = "" then
set n2 = sp4;
elseif n3 = "" then
set n3 = sp4;
elseif n4 = "" then
set n4 = sp4;
elseif n5 = "" then
set n5 = sp4;
elseif n6 = "" then
set n6 = sp4;
end if;
-- 单句处理完成
set i4 = i4 + 1;#循环变量自增1
end while;#结束第四层循环
set i3 = i3 + 1;#循环变量自增1
end while;#结束第三层循环
set i2 = i2 + 1;#循环变量自增1
end while;#结束第二层循环
set i1 = i1 + 1;#循环变量自增1
end while;#结束第一层循环
-- 输出结果
select n1,n2,n3,n4,n5,n6;
/*
显示的值：
n1：猫在院子里玩
n2：看见一只老鼠
n3：竟然有老鼠
n4：猫走过去
n5：猫会吃它吗
n6：肯定会的
*/
END

基础训练7：词语槽
CREATE DEFINER=`root`@`localhost` PROCEDURE `z7`()
BEGIN
-- 基础训练：词语槽
declare say varchar(100);#输入的话语
declare noun1_basket varchar(10);#名词槽1
declare noun2_basket varchar(10);#名词槽2
declare noun3_basket varchar(10);#名词槽3
declare noun4_basket varchar(10);#名词槽4
-- 游标（从词库逐个读取词语）
declare cover int;
declare basket varchar(10);#临时变量，用于存储游标读出的数据
declare sign int default 0;#游标变量，0表示没有没有读取到底，1表示读取到底
declare noun_fish cursor for select word_col from noun;#游标：找名词
declare continue handler for not found set sign = 1;#继续读取，读取到底，使sign值变1
-- 初始化
set say = "熊猫吃竹子";
set noun1_basket = "";
set noun2_basket = "";
set noun3_basket = "";
set noun4_basket = "";
/*
游标找名词，有时会出现一种错误：
例如词语“熊猫”，会被当成三个名词：熊、猫、熊猫，这样就不对了，解决方法：
长词（游标找到的新词）覆盖短词（已找的旧词）：“熊猫”覆盖“熊”和“猫”
长词（已找的旧词）吸收短词（游标找到的新词）：“熊猫”吸收“熊”和“猫”
这样最后只有一个名词：熊猫，不会出现熊、猫
游标新词，赋值或覆盖或被吸收后，变量cover从0变1，这是为了避免向新的空名词槽再次赋值
*/
-- 找名词
set sign = 0;#0表示游标还没有读到底
set basket = "";#游标从表格中，每次读出的一个数据，放入临时变量basket中
open noun_fish;#打开游标
while sign != 1 do#开始循环，游标还没有读到底（sign为0，不为1），就一直循环操作
fetch noun_fish into basket;#把读到的值放入临时变量basket中
-- 如果游标从词语表读出的词，包含在句子中
if instr(say,basket) > 0 then
set cover = 0;#控制basket只能给新的空名词槽赋值一次
-- 名词槽1
if noun1_basket = "" then#第一个槽子就是空的，后面几个槽子更是空的
set noun1_basket = basket;#游标找到名词（熊猫，或熊，或猫），给槽子赋值，占据名词槽1
set cover = 1;#赋值后，占据标志变为1
/*
下面的else，表示第一个槽子已经有值了（之前已被其它名词占据）
那就要考虑词语的覆盖和吸收：
如果这次游标找到的名词是熊猫，而之前槽子里的词是熊，就要长词覆盖短词
如果这次游标找到的名词是熊，而之前槽子里的词是熊猫，就要长词吸收短词
*/
else
/*
instr(basket,noun1_basket) > 0表示basket（游标找到的新词）包含noun1_basket（名词槽1的已有名词）
也就是说，游标找的新词（熊猫）包含槽子里的旧词（熊），所以长词（basket）覆盖短词(noun1_basket)，长词（熊猫）成为槽子里的新词
*/
if instr(basket,noun1_basket) > 0 then
set noun1_basket = basket;
set cover = 1;
/*
instr(noun1_basket,basket) > 0表示noun1_basket（名词槽1已有名词）包含basket（游标找到的新词）
也就是说，槽子里的旧词（熊猫）包含了游标找到的新词（熊），所以长词（noun1_basket）吸收短词（basket），也就是不赋值，保留原值
*/
elseif instr(noun1_basket,basket) > 0 then
set cover = 1;
end if;
end if;
-- 名词槽2
if noun2_basket = "" then
if cover = 0 then
set noun2_basket = basket;
set cover = 1;
end if;
else
if instr(basket,noun2_basket) > 0 then
if cover = 0 then
set noun2_basket = basket;
set cover = 1;
else
set noun2_basket = "";
end if;
elseif instr(noun2_basket,basket) > 0 then
set cover = 1;
end if;
end if;
-- 名词槽3
if noun3_basket = "" then
if cover = 0 then
set noun3_basket = basket;
set cover = 1;
end if;
else
if instr(basket,noun3_basket) > 0 then
if cover = 0 then
set noun3_basket = basket;
set cover = 1;
else
set noun3_basket = "";
end if;
elseif instr(noun3_basket,basket) > 0 then
set cover = 1;
end if;
end if;
-- 名词槽4
if noun4_basket = "" then
if cover = 0 then
set noun4_basket = basket;
set cover = 1;
end if;
else
if instr(basket,noun4_basket) > 0 then
if cover = 0 then
set noun4_basket = basket;
set cover = 1;
else
set noun4_basket = "";
end if;
elseif instr(noun4_basket,basket) > 0 then
set cover = 1;
end if;
end if;
end if;
end while;#结束循环
close noun_fish;#关闭游标
-- 输出结果
select noun1_basket,noun2_basket,noun3_basket,noun4_basket;
/*
noun1_basket：熊猫
noun2_basket：竹子
noun3_basket：空值
noun4_basket：空值
这样就不会出现名词：熊、猫
*/
END

基础训练8：找出话语中的数字
CREATE DEFINER=`root`@`localhost` PROCEDURE `z8`()
BEGIN
-- 基础训练：找数词
-- 先找数词单位，数词单位左边就是数字
declare say varchar(100);#输入的话语
declare num varchar(10);#数字
declare unit varchar(10);#数词单位
declare unit_left varchar(50);#数词单位的左边
declare unit_left_length int;#数词单位的左边的字符串的长度
declare i int;#要检测的字符位置
declare n varchar(3);#单个字符的内容
declare w int;#判断字符是否是数字
-- 游标
declare basket varchar(20);#临时变量，用于存储游标读出的数据
declare sign int default 0;#游标变量，0表示没有没有读取到底，1表示读取到底
declare unit_fish cursor for select unit_col from other_word;#游标：找数词单位
declare continue handler for not found set sign = 1;#继续读取，读取到底，使sign值变1
-- 初始化
set say = "山上有162只猫";
set num = "";
set unit = "";
set unit_left = "";
set unit_left_length = 0;
-- 游标操作
set sign = 0;
set basket = "";
open unit_fish;
while sign != 1 do
fetch unit_fish into basket;
if instr(say,basket) > 0 then#找到数词单位
set unit = basket;
-- 开始找数字
set unit_left = substring_index(say,unit,1);#数词单位左边的字符串
set unit_left_length = char_length(unit_left);#数词单位左边内容的长度。数字在数词单位左边，所以往左边看
set i = 1;#数词单位左边的内容的从右向左数第1个字符，例如64个，i=1时，n的值是4，i=2时，n的值是6
lable_num: begin#设置标记，为了后面跳出循环
-- 循环是逐个字符的操作，从第1个字符（i=1），一直到左边最后一个字符。unit_left_length就是最后一个字符的位置
while(i <= unit_left_length) do
set n = substring(unit_left,-i,1);#该字符的内容，-i是从右向左计算
-- FIND_IN_SET函数判断一个字符（变量n中的字符），是否属于字符集合（双引号里的数字）中的一个
set w = FIND_IN_SET(n,"0,1,2,3,4,5,6,7,8,9,零,一,二,两,三,四,五,六,七,八,九,十,百,千,万,亿,兆");#如果该字符属于集合中的数字，返回值就大于0
if w > 0 then #该字符为数字
set num = concat(n,num);#每次循环新找的数字字符，要与之前的数字字符拼接。因为数字往往不止是一个字符，例如64就是2个字符，需要把6和4拼接到一起
else#遇到不是数字的字符，就该跳出循环了。例如山上有162只猫，从数词单位“只”向左找数字，遇到“有”字，找数字就结束了，找到的数字是162
leave lable_num;#跳出循环
end if;
set i = i + 1;#要检测的字符位置，向左移动一位，为了看看下一个字符是否还是数字
end while;
end lable_num;#跳出循环后，到这里
end if;
end while;
close unit_fish;
-- 输出结果
select num,unit;
/*
num：162
unit：只
*/
END

基础训练9：词性辨析
CREATE DEFINER=`root`@`localhost` PROCEDURE `z9`()
BEGIN
/*
基础训练：动词的词性辨析
词性辨析是多种方法并用，现在介绍其中一种方法
词性辨析表（verb_judge）有4列
id：编号
word_col：要判断的动词，看它到底是不是动词
type_col：动词的左右位置
content_col：动词左右位置的字符
+----------+----------+-------------+
| word_col | type_col | content_col |
+----------+----------+-------------+
| 学 | r1 | 校 |
+----------+----------+-------------+
| 生 | l1 | 花 |
+----------+----------+-------------+
“学”字右边1个字符（r1）是“校”字时，“学”字不做动词，因为“学校”是名词
“生”字左边1个字符（l1）是“花”字时，“生”字不做动词，因为“花生”是名词
*/
declare say varchar(100);#用户输入的话语
declare verb varchar(10);#要判断的动词
declare not_verb int;#是否为动词：0：是动词，1：非动词
declare left_part varchar(50);#分割词（动词）左边句子
declare right_part varchar(50);#分割词右边句子
declare left_1char varchar(3);#分割词左边第1个字符
declare left_2char varchar(6);#分割词左边的2个字符
declare left_3char varchar(9);#分割词左边的3个字符
declare right_1char varchar(3);#分割词右边1个字符
declare right_2char varchar(6);#分割词右边2个字符
declare right_3char varchar(9);#分割词右边3个字符
declare type_data varchar(10);#r1、l1、r2、l2等标识
-- 游标
declare basket varchar(10);
declare sign int default 0;#游标变量，0表示没有没有读取到底，1表示读取到底
declare horse cursor for select content_col from verb_judge where word_col = verb;#游标
declare continue handler for not found set sign = 1;#继续读取，读取到底，使sign值变1
-- 初始化
set say = "美丽的学校";
set verb = "学";
set not_verb = 0;#默认是动词
set left_part = "";
set right_part = "";
set left_1char = "";
set left_2char = "";
set left_3char = "";
set right_1char = "";
set right_2char = "";
set right_3char = "";
set type_data = "";
-- 句子分割
-- verb左边的句子
set left_part = substring(say,1,instr(say,verb)-1);
-- verb右边的句子
set right_part = substring(say,instr(say,verb) + char_length(verb));
-- verb左边的1个字符
set left_1char = substring(left_part,-1,1);
-- verb左边的2个字符
set left_2char = substring(left_part,-2,2);
-- verb左边的3个字符
set left_3char = substring(left_part,-3,3);
-- verb右边的1个字符
set right_1char = substring(right_part,1,1);
-- verb右边的2个字符
set right_2char = substring(right_part,1,2);
-- verb右边的3个字符
set right_3char = substring(right_part,1,3);
-- 按判断表（verb_judge）来判断
-- word_col是要判断的字词（动词），type_col是左右位置，content_col是左右位置的字词，数据符合就不是动词了
set sign = 0;
set basket = "";
open horse;
while sign != 1 do
fetch horse into basket;
-- 变量verb范围里,basket是游标读出的content_col的一个内容。三列条件知道两个，可以找出剩下一列（type_col）的一个内容
set type_data = (select type_col from verb_judge where word_col = verb and content_col = basket limit 1);
if type_data = "r1" and basket = right_1char then#三个数据都对应上了
set not_verb = 1;#不是动词
elseif type_data = "l1" and basket = left_1char then
set not_verb = 1;
elseif type_data = "r2" and basket = right_2char then
set not_verb = 1;
elseif type_data = "l2" and basket = left_2char then
set not_verb = 1;
elseif type_data = "r3" and basket = right_3char then
set not_verb = 1;
end if;
end while;
close horse;
-- 输出数据
select not_verb;
-- not_verb：1，表示“美丽的学校”中的“学”字不是动词
END