MySQL 进阶使用【函数、索引、视图、存储过程、存储函数、触发器】

前言

        做数仓开发离不开 SQL ,写了很多 HQL 回头再看 MySQL 才发现,很多东西并不是 HQL 所独创的,而是几乎都来自于关系型数据库通用的 SQL;想到以后需要每天和数仓打交道,那么不管是 MySQL 还是 Oracle ,都需要深入了解一下,不能只停留在之前的 CRUD 上了。

1、MySQL 函数

1.1、GROUP_CONCAT 聚合函数

1.1.1、语法

GROUP_CONCAT([DISTINCT] 字段名* [ORDER BY DESC|ASC] [SEPARATOR] )

 1.1.2、使用案例

把不同部门的员工合并到一行,按照 id 进行升序排序,并用 '-' 分割开来:

SELECT department,GROUP_CONCAT(DISTINCT emp_id,emp_name ORDER BY emp_id SEPARATOR '-') AS id_with_name
FROM emp
GROUP BY department;

运行结果:

1.2、数学函数

简单的函数没有必要啰嗦,这里只介绍一些常用的:

  • ABS:绝对值
  • CEIL:向上取整
  • FLOOR:向下取整
  • RAND:0~1随机数
  • ROUND(x):四舍五入取整
  • ROUND(x,y):四舍五入保留 y 位小数
  • TRUNCATE(x,y):不四舍五入保留 y 位小数

1.3、字符串函数

1.4、日期函数

1.5、if 函数

MySQL 的 IF 函数和 HQL 是一样的,这里不做介绍。

1.6、窗口函数

        窗口函数一般都是配合聚合函数使用的,毕竟使用窗口就是为了把一类时间或者其它属性有关系的数据联系在一起。这里同样只介绍一些我没用过的函数:

1.6.1、CUME_DIST

用途:分组内小于、等于当前rank值的行数 / 分组内总行数

公式:rank / rows

场景:查询小于等于当前行某个值的记录占总记录的比例

解释:其实就是用 rank 函数的结果 / 记录总数

-- 该员工的薪资超过了相同部门多少人
SELECT emp_id,emp_name,department,salary,ROUND(CUME_DIST() OVER (PARTITION BY department ORDER BY salary),2) AS rate
FROM emp;

1.6.2、FIRST_VALUE 和 LAST_VALUE

用途:分组内的第一条/最后一条记录的某个字段的值

应用场景:截止目前按照xx排序后的第一名/最后一名的值

注意:它是对每一条记录都进行一次计算(相当于 rows between unbounded preceding and current row),而不是全局进行计算(它不会把该行之后考虑进计算范围)

-- 查询出同部门内工资最低和最高的人的薪资
SELECT emp_name,salary,FIRST_VALUE(salary) OVER (PARTITION BY department ORDER BY salary) AS first,LAST_VALUE(salary) OVER (PARTITION BY department ORDER BY salary) AS last
FROM emp;

1.6.3、NTH_VALUE

用途:返回窗口内地 exper 个值。(expr 可以是表达式,也可以是数字)

SELECT emp_name,department,salary,NTH_VALUE(salary,2) over (PARTITION BY department ORDER BY salary) AS second
FROM emp;

1.6.4、NTILE

用途:将分区中的有序数据分为n个等级,记录等级数(等级规则取决于是否分区和排序字段)

应用场景:将员工按照薪资进行划分等级

SELECT emp_name,department,salary,NTILE(3) over (ORDER BY salary) AS grade
FROM emp;

2、视图

2.1、视图的概念

介绍

  • 视图(view)是一个虚拟表,非真实存在,其本质是根据SQL语句获取动态的数据集,并为其命名,用户使用时只需使用视图名称即可获取结果集,并可以将其当作表来使用。
  • 数据库中只存放了视图的定义,而并没有存放视图中的数据。
  • 这些数据存放在原来的表中。 使用视图查询数据时,数据库系统会从原来的表中取出对应的数据。因此,视图中的数据是依赖于原来的表中的数据的。一旦表中的数据发生改变,显示在视图中的数据也会发生改变。

作用

  • 简化代码,可以把重复使用的查询封装成视图重复使用,同时可以使复杂的查询易于理解和使用。
  • 安全原因,如果一张表中有很多数据,很多信息不希望让所有人看到,此时可以使用视图视,如:社会保险基金表,可以用视图只显示姓名,地址,而不显示社会保险号和工资数等,可以对不同的用户,设定不同的视图。

2.2、创建视图

这里只介绍常用的创建方法:

语法

CREATE [OR REPLACE]
VIEW 视图名称
AS SELECT语句

案例

CREATE OR REPLACE
VIEW salary_grade
AS SELECT *, NTILE(3) over (ORDER BY salary) AS grade FROM emp;

2.3、查看所有表和视图

SHOW FULL TABLES;

2.4、修改视图

-- 1.通过alter修改视图
ALTER VIEW 视图名 AS SELECT语句;
-- 2.通过 create or replace 覆盖视图
CREATE OR REPLACE
VIEW 视图名
AS SELECT语句;

某些视图是可更新的,比如基表。对于可更新的视图,在视图中的行和基表中的行之间必须具有一对一的关系。如果视图包含下述结构中的任何一种,那么它就是不可更新的:

  • 聚合函数(SUM(), MIN(), MAX(), COUNT()等)
  • DISTINCT
  • GROUP BY
  • HAVING
  • UNION或UNION ALL
  • 位于选择列表中的子查询
  • JOIN
  • FROM子句中的不可更新视图
  • WHERE子句中的子查询,引用FROM子句中的表。
  • 仅引用文字值(在该情况下,没有要更新的基本表)

注意:视图中虽然可以更新数据,也就是说,可以在UPDATE、DELETE或INSERT等语句中使用它们,以更新基表的内容。但是有很多的限制。一般情况下,最好将视图作为查询数据的虚拟表,而不要通过视图更新数据。因为,使用视图更新数据时,如果没有全面考虑在视图中更新数据的限制,就可能会造成数据更新失败。

-- 往视图中插入数据
INSERT INTO emp_order_by_salary(emp_id, emp_name, salary, department) VALUES (1001,'谢永强',3500,'人事部');
-- 修改视图数据
UPDATE emp_order_by_salary SET emp_name='狄仁杰' WHERE emp_id=1001;
-- 删除视图数据
DELETE FROM emp_order_by_salary WHERE emp_id=1001;

注意:

  • 视图永远只是一个虚拟表,不存储数据,修改视图就是修改基表!

3、存储过程

3.1、存储过程介绍

什么是存储过程

  • MySQL 5.0 版本开始支持存储过程。
  • 简单的说,存储过程就是一组SQL语句集,功能强大,可以实现一些比较复杂的逻辑功能,类似于JAVA语言中的方法;
  • 存储过就是数据库 SQL 语言层面的代码封装与重用。

特性

  • 有输入输出参数,可以声明变量,有if/else, case,while等控制语句,通过编写存储过程,可以实现复杂的逻辑功能;
  • 函数的普遍特性:模块化,封装,代码复用;
  • 速度快,只有首次执行需经过编译和优化步骤,后续被调用可以直接执行,省去以上步骤;

其实也就是把可以实现某一功能的一堆 SQL 封装起来,等到需要用的时候直接调用即可,大大提高了SQL 的复用。

3.2、入门案例

为了区分普通的 SQL ,我们需要自定义 SQL 的结束符。下面我们看看存储过程的基本语法:

delimiter 自定义结束符号
create procedure 储存名([ in ,out ,inout ] 参数名 数据类形...)
beginsql语句
end 自定义的结束符号
delimiter ; -- 恢复为原本的结束符 ';'

自定义存储过程:

-- 自定义存储过程
DELIMITER $$
CREATE PROCEDURE func01()
BEGINSELECT * FROM emp;
END $$
DELIMITER ;-- 调用存储过程
call func01();

3.3、存储过程的使用

3.3.1、局部变量的定义

  • 局部变量是用户自定义,在 begin/end 快中有效
1)使用 declare 声明局部变量
declare 变量名 变量类型 [default 默认值]

测试: 

DELIMITER $$
CREATE PROCEDURE func2()
BEGINdeclare tmp int default 1;set tmp = 2;SELECT tmp;
end $$
DELIMITER ;call func2();    -- 2
2)使用 select into 语句

注意:select into 只能给变量赋值,变量依然需要使用 declare 声明!

语法

-- 把查询结果赋值给变量
SELECT col1, col2 into var1, var2 from table where condition;

注意:返回结果只能是单行结果!

测试:

DELIMITER $$
CREATE PROCEDURE func03()
BEGINdeclare eid int;declare ename varchar(20);declare esalary double;declare dname varchar(20);SELECT emp_id, emp_name, salary, departmentINTO eid,ename,esalary,dnameFROM emp WHERE emp_id=1;SELECT eid,ename,esalary,dname;
END $$
DELIMITER ;call func03();

注意:局部变量名不能和表字段相同,否则查询为 null (字段类型可以不同)。

3.3.2、用户变量的定义

语法

@@变量名

注意

  • 不需要像局部变量一样提前声明!(要声明也可以 直接 set @变量名 = xxx)
  • 生命周期是当前会话
DELIMITER $$
CREATE PROCEDURE func04()
BEGINSET @result = 1;
end $$
delimiter ;call func04();SELECT @result; -- 必须调用之后才能被初始化赋值 否则为null

3.3.3、系统变量

  • 系统变量又分为全局变量会话变量
  • 全局变量在 MYSQL 启动的时候由服务器自动将它们初始化为默认值,这些默认值可以通过更改my.ini这个文件来更改。
  • 会话变量在每次建立一个新的连接的时候,由MYSQL来初始化。MYSQL会将当前所有全局变量的值复制一份。来做为会话变量。
  • 也就是说,如果在建立会话以后,没有手动更改过会话变量与全局变量的值,那所有这些变量的值都是一样的。
  • 全局变量与会话变量的区别就在于,对全局变量的修改会影响到整个服务器,但是对会话变量的修改,只会影响到当前的会话(也就是当前的数据库连接)。
  • 有些系统变量的值是可以利用语句来动态进行更改的,但是有些系统变量的值却是只读的,对于那些可以更改的系统变量,我们可以利用set语句进行更改。
1. 全局变量

注意:全局变量由系统提供,整个数据库有效。

语法

@@global.变量名

查看全局变量:

show global variables ;

查看某个全局变量的值:

SELECT @@global.binlog_format;

设置全局变量(两种方式):

set global sort_buffer_size = 40000;
set @@global.sort_buffer_size = 40000;
2. 会话变量

语法:

@@session.变量名

查看会话变量:

show session variables;

查看某个会话变量的值:

SELECT @@session.auto_increment_increment;

修改会话变变量的值(两种方法):

set session sort_buffer_size = 50000;
set @@session.sort_buffer_size = 50000;

3.3.4、存储过程传参 in

in 表示传入的参数, 可以传入数值或者变量,即使传入变量,并不会更改变量的值,可以内部更改,仅仅作用在函数范围内。

-- in 传参
delimiter $$
create procedure func05(in id int)
beginselect * from emp where emp.emp_id = id;
end $$
delimiter ;call func05(1); -- 查询 emp_id = 1 的员工信息

3.3.5、存储过程传参 out

out 表示从存储过程内部传值给调用者(内部值指的是用户变量),用户变量作用于整个会话,所以调用这个方法时,用户变量就会被初始化并赋值。

delimiter $$
create procedure func06(in id int,out ename varchar(20))
beginselect emp_name into ename from emp where emp_id=id;
end $$
delimiter;call func06(1,@ename); -- 这里的@name是用户变量所以并不需要声明select @ename; -- 张晶晶

3.3.6、存储过程传参 inout

inout 表示从外部传入的参数经过修改后可以返回的变量,既可以使用传入变量的值也可以修改变量的值(即使函数执行完)。

说人话就是:这个参数既可以当做普通参数用,也可以当做用户变量对它进行赋值

delimiter $$
create procedure func07(inout id int,inout name varchar(20))
beginupdate emp set emp_name = name where emp_id = id;
end $$
delimiter ;set @name = '李大喜';
set @id = 1;
call func07(@id,@name);select * from emp where emp_id = 1; -- 张晶晶变成了李大喜

3.3.7、存储过程传参总结

  • in 输入参数,意思说你的参数要传到存过过程的过程里面去,在存储过程中修改该参数的值不能被返回
  • out 输出参数:该值可在存储过程内部被改变,并向外输出
  • inout 输入输出参数,既能输入一个值又能传出来一个值

3.3.8、存储过程流程控制 - if 语句

DELIMITER $$
CREATE PROCEDURE func08(in id int)
begindeclare num int default 0;declare result varchar(50);select count(*) into num from emp;if id < 1 || id > numthen set result = '超出范围';elseset result = (select emp_name from emp where emp_id = id);end if;select result;
end $$
delimiter ;call func08(1);

3.3.9、存储过程控制流程 - case 语句

需要注意的是 case 语句有两种语法:一种是匹配变量是否等于某个值,一种是匹配变量是否符合某种条件。

匹配条件

DELIMITER $$
CREATE PROCEDURE func09(in salary int)
BEGINDECLARE grade VARCHAR(3);CASEWHEN salary > 5000 THEN SET grade = '高';WHEN salary > 3000 THEN SET grade = '中';ELSE SET grade = '低';END CASE;SELECT grade;
END $$
DELIMITER ;CALL func09(8000); -- 高

匹配值: 

delimiter $$
create procedure func10(in grade varchar(1))
begindeclare result varchar(20);case gradewhen 'A' then set result = '90~100';when 'B' then set result = '80~90';when 'C' then set result = '70~80';when 'D' then set result = '60~70';when 'E' then set result = '0~60';else set result = '参数错误';end case;select result;
end $$
delimiter ;call func10('B');

3.3.10、循环语句和循环控制

先介绍循环控制:

  • leave:类似于 break
  • iterate:类似于 continue
1. while 循环

语法

[标签]: while 循环条件 do循环体
end while [标签];

测试:

delimiter $$
create procedure func11()
begindeclare i int default 0;declare num int default (select count(*) from emp);while i<num doselect i;set i = i+1;end while;
end $$
delimiter ;call func11();

测试结果就是会创建 num 张表,表内就是 i 的值。

2. repeat 循环

语法

[标签:] repeat循环体;
until 条件表达式
end repeat 标签;

测试:

delimiter $$
create procedure func12()
begindeclare i int default 0;label: repeatset i = i+1;until i > 10end repeat label;select '循环结束';
end $$
delimiter ;call func12();
3. loop 循环

语法

[标签:] loop循环体if 条件表达式 thenleave 标签;endif;
end loop;

测试:

delimiter $$
create procedure func13()
begindeclare i int default 0;label: loopif i!=5 thenset i = i+1;else leave label;end if;end loop;
end $$
delimiter ;call func13();

3.3.11、存储过程之游标(Cursor)

        游标(cursor)是用来存储查询结果集的数据类型 , 在存储过程和函数中可以使用光标对结果集进行循环的处理。光标的使用包括光标的声明、OPEN、FETCH 和 CLOSE.

语法

-- 声明语法
declare cursor_name cursor for select_statement
-- 打开语法
open cursor_name
-- 取值语法
fetch cursor_name into var_name [, var_name] ...
-- 关闭语法
close cursor_name

测试:

-- 游标
delimiter $$
create procedure func14(in id int)
begin-- 声明局部变量declare eid int;declare ename char(20);declare esalary decimal(10,2);-- 声明游标declare my_cursor cursor forselect emp_id,emp_name,salaryfrom empwhere emp_id=id;-- 打开游标open my_cursor;-- 通过游标获取每一行label: loopfetch my_cursor into eid,ename,esalary;select eid,ename,esalary;leave label;end loop;-- 关闭游标close my_cursor;
end $$
delimiter ;drop procedure func14;call func14(1);

注意循环体中必须有退出的条件,否则就是死循环!

3.3.12、句柄 handler

4、存储函数(自定义函数)

注意:自定义函数之前必须设置全局变量:

-- 信任函数的创建者
set global log_bin_trust_function_creators=TRUE;
-- 信任函数的创建者
set global log_bin_trust_function_creators=TRUE;delimiter $$
create function oneToNum(n int) returns int
begindeclare sum int default 0;while n!=0 doset sum = sum + n;set n = n-1;end while;return sum;
end $$
delimiter ;select oneToNum(3); --6

注意:自定义函数不能包含递归,递归需要使用专门的语法。

5、触发器

5.1、介绍

  • 触发器,就是一种特殊的存储过程。触发器和存储过程一样是一个能够完成特定功能、存储在数据库服务器上的SQL片段,但是触发器无需调用,当对数据库表中的数据执行DML操作时自动触发这个SQL片段的执行,无需手动条用。
  • 在MySQL中,只有执行insert,delete,update操作时才能触发触发器的执行
  • 触发器的这种特性可以协助应用在数据库端确保数据的完整性 , 日志记录 , 数据校验等操作 。
  • 使用别名 OLD 和 NEW 来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发。

5.2、触发器的特性

  • 什么条件会触发:I、D、U
  • 什么时候触发:在增删改前或者后
  • 触发频率:针对每一行执行
  • 触发器定义在表上,附着在表上

5.3、触发器语法

5.3.1、创建只有一个执行语句方触发器

注意:这里的触发事件只能是 insert、update、delete。

create trigger 触发器名 before|after 触发事件
on 表名 for each row 
执行语句;

5.3.2、创建有多个执行语句的触发器

create trigger 触发器名 before|after  触发事件 
on 表名 for each row
begin执行语句列表
end;

5.4、触发器的使用

5.4.1.创建触发器

-- 触发器
drop trigger if exists trigger_emp;-- 创建受触发器影响的表格
create table emp_log(id int primary key auto_increment,time timestamp,log_text varchar(255)
);-- 创建触发器
create trigger trigger_emp
after insert on emp
for each row
insert into emp_log values (NULL,now(),'新的员工注册');insert into emp values (NULL,'刘海柱',8999,'技术部');

当向 emp 表进行 insert 操作时,就会触发触发器向 emp_log 插入一条日志。

5.4.2、NEW 和 OLD

MySQL 中定义了 NEW 和 OLD,用来表示触发器的所在表中,触发了触发器的那一行数据,来引用触发器中发生变化的记录内容,具体地:

        这让我很自然的联想到了 MySQL 的 binlog 功能,我们数仓中使用 MaxWell 来监听 binlog 实现数据同步,但是 binlog 的底层并不是触发器。

语法

NEW.列名
OLD.列名

测试:

-- 不要影响到其它触发器对 emp_log 的操作,比如这里增加了两个字段,
-- 当对该表进行insert 操作时,因为给 emp_log 增加字段之后没有更新触发器的行为
-- 就会导致给 emp 和 emp_log 插入数据是全部失败alter table emp_log add old varchar(50);
alter table emp_log add new varchar(50);create trigger trigger_test
after update
on emp for each row
insert into emp_log values (null,now(),concat('更新数据'),concat(OLD.emp_id,OLD.emp_name,OLD.salary,OLD.department),concat(NEW.emp_id,NEW.emp_name,NEW.salary,NEW.department)
);
update emp set emp_name = '李元芳' where emp_id = 3;select * from emp_log;

5.5、触发器使用的注意事项

  • MYSQL中触发器中不能对本表进行 insert ,update ,delete 操作,以免递归循环触发
  • 尽量少使用触发器,假设触发器触发每次执行1s,insert table 500条数据,那么就需要触发500次触发器,光是触发器执行的时间就花费了500s,而insert 500条数据一共是1s,那么这个insert的效率就非常低了。
  • 触发器是针对每一行的;对增删改非常频繁的表上切记不要使用触发器,因为它会非常消耗资源。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/838508.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EDA设计学习笔记2:STM32F103C8T6最小系统板的仿绘

今日开始仿制练习一个STM32F103C8T6最小系统板&#xff0c;通过对这个最小系统板的仿制&#xff0c;达到对自己PCB设计的练习的目的&#xff0c;最终目标是自己设计出一块PCB&#xff0c;做一个OLED的桌面小摆件...... 也不知道画出来能不能用..... 目录 主控芯片的搜索与放置…

迷宫游戏(c++)

我们来玩一个迷宫游戏&#xff0c;尝试走一下面的迷宫。 迷宫游戏 我们用一个二维的字符数组来表示前面画出的迷宫&#xff1a; S**. .... ***T 其中字符S表示起点&#xff0c;字符T表示终点&#xff0c;字符*表示墙壁&#xff0c;字符.表示平地。你需要从S出发走到T&#xf…

React渲染流程

在 React 渲染分为两个阶段&#xff0c;Render 和 Commit&#xff0c;Render 是修改 React 组件的状态&#xff0c;把需要更新的组件标记为待更新&#xff0c;在 Commit 阶段将待更新的组件进行渲染并最终更新到浏览器的 Dom 树中。 Render 阶段是可以并执行操作的&#xff0c…

软件测试常见面试题合集(内附详细答案)

01 软件测试理论部分 1.1 测试概念 1. 请你分别介绍一下单元测试、集成测试、系统测试、验收测试、回归测试 单元测试&#xff1a;完成最小的软件设计单元&#xff08;模块&#xff09;的验证工作&#xff0c;目标是确保模块被正确的编码 集成测试&#xff1a;通过测试发现与…

猛兽派对是什么游戏 猛兽派对攻略大全 苹果电脑怎么玩《猛兽派对》?

猛兽派对是多人派对类型的游戏&#xff0c;该款游戏的动作基于物理原理设计的&#xff0c;体验游戏玩家可以选择自己喜欢的小动物角色参加派对&#xff0c;游戏内具有很多不同的关卡可供挑战。 在steam平台上&#xff0c;猛兽派对对应英文名称是PartyAnimals&#xff0c;官方正…

JVM内存模型最新面试题(持续更新)

问题&#xff1a;java中创建的对象一般放在哪里&#xff1f;(全流程包含从创建到回收) 回答 大部分对象在堆中&#xff0c;这个基本都知道&#xff1b; 少部分对象是会在栈中的&#xff0c;比如作用域不局限于方法内的方法内部变量&#xff0c;这类对象的特征一般就是生命周期…

opencv4.8.0 GPU版本各平台编译

一、opencv4.8.0 ubuntu22.04上编译&#xff1a; 用cmake进行编译,需要配置三次。选中world选项&#xff0c;输入opencv_contrib_module路径。 ubuntu22.04上编译&#xff1a; cmake \ -D CMAKE_BUILD_TYPERELEASE \ -D CMAKE_INSTALL_PREFIX/usr/local \ -D BUILD_opencv_p…

免费公有云轻量级云服务,支持免费云数据库和创建应用,支持多语言应用!

在今天这个数字化迅速发展的时代&#xff0c;选择一个高效、经济且功能全面的云平台对于任何大小的企业来说都是至关重要的。MemFire Cloud 作为市场上新兴的星级云服务提供商&#xff0c;以其免费的公有云服务和对多种编程语言的支持&#xff0c;提供了一站式解决方案&#xf…

51单片机超声波测距_液位检测_温度检测原理图PCB仿真代码

目录 实物图&#xff1a; PCB ​原理图​ 仿真图 ​编辑 程序 资料下载地址&#xff1a;51单片机超声波测距-液位检测-温度检测原理图PCB仿真代码 主控为stc89c52,通过ds18b20进行温度采集&#xff0c;超声波测距&#xff0c;距离不可以超过1m&#xff0c;通过按键可以设…

CAD插入文字到另一图形样式变相同

CAD从一张图形复制到另外一张图形后&#xff0c;文字样式变成一样是因为两张图所用的文字样式名称一样&#xff0c;但是样式里面的使用字体样式不一样。如下图所示&#xff0c;找到工具栏中的注释 &#xff0c;点击文字样式。里面就会显示当前图形中使用的样式名称及其对应的字…

微信小程序如何使用weui组件库?

一、方法一&#xff1a;通过npm安装 通过npm构建方式引入weui组件库 &#xff08;找到.eslintrc.js 右键&#xff0c;在内件终端打开&#xff09;打开命令提示符后&#xff0c;输入 &#xff08;1&#xff09;npm init -y来快速生成一个默认的package.json文件 &#xff08;…

鸿蒙 装饰器@builder 使用中的问题 以及解决方案

builder装饰器 一 介绍Builder装饰器&#xff1a;自定义构建函数二 问题点三 解决方法四 仓库地址 一 介绍Builder装饰器&#xff1a;自定义构建函数 用于填充UI组件 开发者可以将重复使用的UI元素抽象成一个方法&#xff0c;在build方法里调用。 根据场景分类 组件内自定义构…

设计非递归算法,编程:在二叉排序树中,打印关键码a, b的公共祖先。注:例,若a是b的祖先,则a不算作公共祖先。反之亦然。

二叉排序树&#xff1a; 代码&#xff1a; #include <iostream> using namespace std;// 定义二叉树节点结构 typedef struct BTNode {char show;struct BTNode* left;struct BTNode* right; } BTNode;// 非递归插入节点的函数 BTNode* insertNode(BTNode* root, char k…

Leetcode - 周赛397

目录 一&#xff0c;3146. 两个字符串的排列差 二&#xff0c;3147. 从魔法师身上吸取的最大能量 三&#xff0c;3148. 矩阵中的最大得分 四&#xff0c;3149. 找出分数最低的排列 一&#xff0c;3146. 两个字符串的排列差 本题就是求同一个字符在两个字符串中的下标之差的…

QCustomPlot的了解

&#xff08;一&#xff09;QCustomPlot常见属性设置、多曲线绘制、动态曲线绘制、生成游标、矩形放大等功能实现-CSDN博客 关键代码&#xff1a; QT core gui printsupport 使用上面文章中的代码跑起来的程序效果图&#xff1a; 我的学习过程&#xff1a; 最开始初…

家用充电桩远程监控安全管理系统解决方案

家用充电桩远程监控安全管理系统解决方案 在当今电动汽车日益普及的背景下&#xff0c;家用充电桩的安全管理成为了广大车主关注的重点问题。为了实现对充电桩的高效、精准、远程监控&#xff0c;一套完善的家用充电桩远程监控安全管理系统解决方案应运而生。本方案旨在通过先…

如何去除字符串两侧的空白字符?

TRIM函数会去掉字符串左侧和右侧的空格&#xff0c;语法是&#xff1a;TRIM(字符串) excel中&#xff0c;TRIM函数能去掉字符串左侧和右侧的空格&#xff0c;它的ASCII码是32。 以下设定一个字符串组合&#xff0c;它的第一个字符中空格&#xff0c;最后一个字符是换行符 &q…

【Linux进程通信 —— 管道】

Linux进程通信 —— 管道 进程间通信介绍进程间通信的概念进程间通信的目的进程间通信的本质进程间通信的分类 管道什么是管道匿名管道匿名管道的原理pipe用fork来共享管道原理站在文件描述符角度-深度理解管道站在内核角度-管道本质管道读写规则管道的特点管道的四种特殊情况管…

2024软件测试必问的常见面试题1000问!

01、您所熟悉的测试用例设计方法都有哪些&#xff1f;请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。 答&#xff1a;有黑盒和白盒两种测试种类&#xff0c;黑盒有等价类划分法&#xff0c;边界分析法&#xff0c;因果图法和错误猜测法。白盒有逻辑覆盖法&…

三子棋游戏

1、实现三子棋 test.c //测试游戏的逻辑 game.c //游戏代码的实现 game.h //游戏代码的声明&#xff08;函数声明&#xff0c;符号定义&#xff09; 1、 在主函数里实现&#xff0c;首先有个界面&#xff0c;让玩家选择是否玩游戏。上来就先打印菜单&#xff0c;用do ... …