Shell、MySQL常用命令、Java相关
Shell
vim
- 行号
1、显示行号 :set nu
2、取消显示行号 :set nonu
- VIM 全局替换
1、基本的替换
1.1 :s/vivian/sky/ 替换当前行第一个 vivian 为 sky
1.2 :s/vivian/sky/g 替换当前行所有 vivian 为 sky
1.3 :n,$s/vivian/sky/ 替换第 n 行开始到最后一行中每一行的第一个 vivian 为 sky
1.4 :n,$s/vivian/sky/g 替换第 n 行开始到最后一行中每一行所有 vivian 为 sky(n 为数字,若 n 为 .,表示从当前行开始到最后一行)
1.5 :%s/vivian/sky/(等同于 :g/vivian/s//sky/)替换每一行的第一个 vivian 为 sky
1.6 :%s/vivian/sky/g(等同于 :g/vivian/s//sky/g) 替换每一行中所有 vivian 为 sky
2、可以使用 # 作为分隔符,此时中间出现的 / 不会作为分隔符
2.1 :s#vivian/#sky/# 替换当前行第一个 vivian/ 为 sky/
2.2 :%s+/oradata/apras/+/user01/apras1+ (使用+ 来替换 / ): /oradata/apras/替换成/user01/apras1/
cp
复制文件,定义新文件名称 cp a.txt b.txt
复制目录,定义新目录名称 cp -r doc1 doc2
tar
压缩:
压缩文件 tar -czf file.tar.gz file.txt
压缩目录 tar -czf doc.tar.gz doc
解压:
解压至指定目录 tar -xzvf file.tar.gz -C /home/test
解压至当前目录 tar -xzvf file.tar.gz
touch
创建文件
mkdir
创建目录
- 创建目录
mkdir dir1
docker
docker导出容器内部日志信息到本地
# 导出容器最近1个小时的日志信息
docker logs --since 60m 23ab5c69fc39 >> log.txt
# 导出容器某一时间段的日志信息
docker logs --since "2022-12-21T10:00:00" --until "2022-12-21T11:00:00" 23ab5c69fc39 >> log.txt
列出指定的容器的端口映射,或者查找将PRIVATE_PORT NAT到面向公众的端口。
# docker port kafka
9092/tcp -> 0.0.0.0:9092
MySQL
使用mysql
- 展示数据库,使用数据库;展示数据表。
mysql>
SHOW DATABASES;
+----------------------------+
| Database |
+----------------------------+
| blog |
| bookshop |
| boot_cache |
| bootvue |
| device |
| ems |
| febs_security |
| haiyu |
| information_schema |
| ldms |
| learn |
| mmt_cloud |
| mydb |
| mysql |
| nacos_config |
| performance_schema |
| practice_teaching_base_ems |
| security |
| spring_cloud |
| ssm |
| sys |
+----------------------------+
21 rows in set
(0.00 sec)
mysql>
USE learn;
Database changed
mysql>
SHOW TABLES;
+-----------------+
| Tables_in_learn |
+-----------------+
| city |
| dept |
| emp |
| emp_bak |
| myview1 |
| pro |
| salgrade |
| t_act |
| t_user |
| user |
| user_tbl |
+-----------------+
11 rows in set
(0.00 sec)
- 显示表列
1、SHOW COLUMNS FROM emp;
2、DESCRIBE emp;
mysql>
DESCRIBE emp;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| EMPNO | int | NO | PRI | NULL | |
| ENAME | varchar(10) | YES | | NULL | |
| JOB | varchar(9) | YES | | NULL | |
| MGR | int | YES | | NULL | |
| HIREDATE | date | YES | | NULL | |
| SAL | double(7,2) | YES | | NULL | |
| COMM | double(7,2) | YES | | NULL | |
| DEPTNO | int | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
8 rows in set
(0.00 sec)
检索数据
- 检索单列
- 检索多列(,)
- 检索所有列(*)
- 检索不同的行
mysql>
SELECT deptno
FROM emp;
+--------+
| deptno |
+--------+
| 20 |
| 30 |
| 30 |
| 20 |
| 30 |
| 30 |
| 10 |
| 20 |
| 10 |
| 30 |
| 20 |
| 30 |
| 20 |
| 10 |
+--------+
14 rows in set
(0.00 sec)
# 解决办法是使用DISTINCT关键字,顾名思义,此关键字指示MySQL只返回不同的值。
mysql>
SELECT DISTINCT deptno
FROM emp;
+--------+
| deptno |
+--------+
| 20 |
| 30 |
| 10 |
+--------+
3 rows in set
(0.00 sec)
- 限制结果
mysql>
SELECT ename
FROM emp
LIMIT 5;
+--------+
| ename |
+--------+
| SMITH |
| ALLEN |
| WARD |
| JONES |
| MARTIN |
+--------+
5 rows in set
(0.01 sec)
# LIMIT 5指示MySQL返回不多于5行。
mysql>
SELECT ename
FROM emp
LIMIT 3, 4;
mysql>
SELECT ename
FROM emp
LIMIT 4 OFFSET 3;
+--------+
| ename |
+--------+
| JONES |
| MARTIN |
| BLAKE |
| CLARK |
+--------+
4 rows in set
(0.00 sec)
# LIMIT 3, 4(LIMIT 4 OFFSET 3)指示MySQL返回从行3开始的4行。
排序检索数据
- 排序数据(ORDER BY)
- 按多个排序(排序按顺序进行)
- 指定排序方向(DESC降序,默认ASC升序)
过滤数据
-
使用WHERE子句
-
WHERE子句操作符(<>、!=不等于,BETWEEN在指定的两个值之间)
mysql>
SELECT *
FROM emp
WHERE sal BETWEEN 1000 AND 2000;
+-------+--------+----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+----------+------+------------+---------+---------+--------+
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+----------+------+------------+---------+---------+--------+
6 rows in set
(0.00 sec)
- 空值检查(IS NULL)
mysql>
SELECT *
FROM emp
WHERE mgr IS NULL;
+-------+-------+-----------+------+------------+---------+------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+-------+-----------+------+------------+---------+------+--------+
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
+-------+-------+-----------+------+------------+---------+------+--------+
1 row in set
(0.00 sec)
-
OR操作符
-
计算次序(OR操作符优先级高于AND操作符)
在WHERE子句中使用圆括号 任何时候使用具有AND和OR操作符的WHERE子句,都应该使用圆括号明确地分组操作符。不要过分依赖默认计算次序,即使它确实是你想要的东西也是如此。使用圆括号没有什么坏处,它能消除歧义。
-
IN操作符(指定条件范围)
-
NOT操作符(有且只有一个功能,否定它之后所跟的任何条件)
用通配符进行过滤
-
LIKE操作符
- 通配符(wildcard)
- %(表示任何字符出现任意次数,%不能匹配NULL。)
- _(总是匹配单个字符)
- 搜索模式(search pattern)
- 通配符(wildcard)
-
使用通配符的技巧
正如所见, MySQL的通配符很有用。但这种功能是有代价的:通配符搜索的处理一般要比前面讨论的其他搜索所花时间更长。这里给出一些使用通配符要记住的技巧。
- 不要过度使用通配符。如果其他操作符能达到相同的目的,应该
使用其他操作符。- 在确实需要使用通配符时,除非绝对有必要,否则不要把它们用
在搜索模式的开始处。把通配符置于搜索模式的开始处,搜索起
来是最慢的。- 仔细注意通配符的位置。如果放错地方,可能不会返回想要的数据。
用正则表达式进行搜索
- 基本字符匹配
mysql>
SELECT ename
FROM emp
WHERE ename REGEXP 'O';
+-------+
| ename |
+-------+
| JONES |
| SCOTT |
| FORD |
+-------+
3 rows in set
(0.14 sec)
# 除关键字LIKE被REGEXP替代外,这条语句看上去非常像使用LIKE的语句。
# LIKE匹配整个列。如果被匹配的文本在列值中出现,LIKE将不会找到它,相应的行也不被返回(除非使用通配符)。而REGEXP在列值内进行匹配,如果被匹配的文本在列值中出现, REGEXP将会找到它,相应的行将被返回。这是一个非常重要的差别。
- 进行OR匹配(使用 |,可以给出两个以上的OR条件,如:'1000 | 2000 | 3000')
- 匹配几个字符之一(使用 [ ])
- 匹配范围([0-9],[a-z])
- 匹配特殊字符(
\\.表示查找. \\-表示查找-
) - 匹配字符类
- 匹配多个实例
- 定位符
创建计算字段
- 拼接
mysql>
SELECT concat(ename, '(', sal, ')') AS 'emp(sal)'
FROM emp
ORDER BY sal;
+-----------------+
| emp(sal) |
+-----------------+
| SMITH(800.00) |
| JAMES(950.00) |
| ADAMS(1100.00) |
| WARD(1250.00) |
| MARTIN(1250.00) |
| MILLER(1300.00) |
| TURNER(1500.00) |
| ALLEN(1600.00) |
| CLARK(2695.00) |
| SCOTT(3000.00) |
| FORD(3000.00) |
| BLAKE(3135.00) |
| JONES(3272.50) |
| KING(5000.00) |
+-----------------+
14 rows in set
(0.00 sec)
- 使用别名(AS)
- 执行算术计算(列名进行+-*/)
使用数据处理函数
- 文本处理函数
函数 | 说明 |
---|---|
Left() | 返回串左边的字符 |
Length() | 返回串的长度 |
Locate() | 找出串的一个子串 |
Lower() | 将串转换为小写 |
LTrim() | 去掉串左边的空格 |
Right() | 返回串右边的字符 |
RTrim() | 去掉串右边的空格 |
Soundex() | 返回串的SOUNDEX值 |
SubString() | 返回子串的字符 |
Upper() | 将串转换为大写 |
- 日期和时间处理函数
# 匹配日期
mysql>
SELECT *
FROM emp
WHERE date(hiredate) = '87-04-19';
+-------+-------+---------+------+------------+---------+------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+-------+---------+------+------------+---------+------+--------+
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
+-------+-------+---------+------+------------+---------+------+--------+
1 row in set
(0.00 sec)
# 匹配日期范围
mysql>
SELECT *
FROM emp
WHERE year(hiredate) = '1981'
and month(hiredate) = '02';
+-------+-------+----------+------+------------+---------+--------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+-------+----------+------+------------+---------+--------+--------+
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
+-------+-------+----------+------+------------+---------+--------+--------+
2 rows in set
(0.02 sec)
如果要的是日期,请使用Date() 如果你想要的仅是日期,则使用Date()是一个良好的习惯,即使你知道相应的列只包含日期也是如此。这样,如果由于某种原因表中以后有日期和时间值,你的SQL代码也不用改变。当然,也存在一个Time()函数,在你只想要时间时应该使用它。
Date()和Time()都是在MySQL 4.1.1中第一次引入的。
- 数值处理函数
函数 | 说明 |
---|---|
Abs() | 返回一个数的绝对值 |
Cos() | 返回一个角度的余弦 |
Exp() | 返回一个数的指数值 |
Mod() | 返回除操作的余数 |
Pi() | 返回圆周率 |
Rand() | 返回一个随机数 |
Sin() | 返回一个角度的正弦 |
Sqrt() | 返回一个数的平方根 |
Tan() | 返回一个角度的正切 |
汇总数据
- 聚集函数
- AVG()
- COUNT()
- MAX()
- MIN()
- SUM()
- 聚集不同值
mysql>
select avg(distinct sal) as avg_sal
from emp;
+-------------+
| avg_sal |
+-------------+
| 2133.541667 |
+-------------+
1 row in set
(0.00 sec)
- 组合聚集函数
mysql>
select count(*) as num_items, min(sal) as min_sal, max(sal) as max_sal, avg(sal) as avg_sal
from emp;
+-----------+---------+---------+-------------+
| num_items | min_sal | max_sal | avg_sal |
+-----------+---------+---------+-------------+
| 14 | 800.00 | 5000.00 | 2132.321429 |
+-----------+---------+---------+-------------+
1 row in set
(0.00 sec)
分组数据
-
数据分组( count(*) )
-
创建分组(GROUP BY)
mysql>
select sum(sal), avg(sal), count(*)
from emp
group by deptno;
+----------+-------------+----------+
| sum(sal) | avg(sal) | count(*) |
+----------+-------------+----------+
| 11172.50 | 2234.500000 | 5 |
| 9685.00 | 1614.166667 | 6 |
| 8995.00 | 2998.333333 | 3 |
+----------+-------------+----------+
3 rows in set
(0.00 sec)
- 过滤分组(HAVING)
- WHERE过滤行,HAVING过滤分组;
- WHERE在数据分组前过滤,HAVING在数据分组后过滤。
mysql>
select count(*)
from emp
group by deptno
having count(*) > 3;
+----------+
| count(*) |
+----------+
| 5 |
| 6 |
+----------+
2 rows in set
(0.01 sec)
- 分组和排序(GROUP BY 和 ORDER BY)
- SELECT字句顺序
子句 | 说明 | 是否必须使用 |
---|---|---|
SELECT | 要返回的列或表达式 | 是 |
FROM | 从中检索数据的表 | 仅在从表选择数据时使用 |
WHERE | 行级过滤 | 否 |
GROUP BY | 分组说明 | 仅在按组计算聚集时使用 |
HAVING | 组级过滤 | 否 |
ORDER BY | 输出排序顺序 | 否 |
LIMIT | 要检索的行数 | 否 |
使用子查询
- 子查询(结合IN操作符使用)
- 作为计算字段使用子查询
联结表
- 创建联结(WHERE指定联结条件)
- 笛卡尔积
由没有联结条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。
应该保证所有联结都有WHERE子句,否则MySQL将返回比想要的数据多得多的数据。同理,应该保证WHERE子句的正确性。不正确的过滤条件将导致MySQL返回不正确的数据。
消除笛卡尔积最简单最直接的方式:使用等值连接。
mysql>
select dname, ename
from dept,
emp
where dept.deptno = emp.deptno;
+------------+--------+
| dname | ename |
+------------+--------+
| RESEARCH | SMITH |
| SALES | ALLEN |
| SALES | WARD |
| RESEARCH | JONES |
| SALES | MARTIN |
| SALES | BLAKE |
| ACCOUNTING | CLARK |
| RESEARCH | SCOTT |
| ACCOUNTING | KING |
| SALES | TURNER |
| RESEARCH | ADAMS |
| SALES | JAMES |
| RESEARCH | FORD |
| ACCOUNTING | MILLER |
+------------+--------+
14 rows in set
(0.00 sec)
- 内部联结(INNER JOIN)
ANSI SQL规范首选INNER JOIN语法。此外,尽管使用WHERE子句定义联结的确比较简单,但是使用明确的联结语法能够确保不会忘记联结条件,有时候这样做也能影响性能。
- 联结多个表
MySQL在运行时关联指定的每个表以处理联结。这种处理可能是非常耗费资源的,因此应该仔细,不要联结不必要的表。联结的表越多,性能下降越厉害。
创建高级联结
-
使用表别名
-
使用不同类型的联结
- 自联结
使用自联结而不用子查询:
自联结通常作为外部语句用来替代从相同表中检索数据时使用的子查询语句。虽然最终的结果是相同的,但有时候处理联结远比处理子查询快得多。
- 自然联结(自然联结排除多次出现,使每个列只返回一次。一般使用通配符:SELECT *)
- 外部联结
- 左连接(LEFT JOIN)
- 右链接(RIGHT JOIN)
-
使用带聚集函数的联结
-
使用联结和联结条件(应该总是提供联结条件,否则会得出笛卡尔积。)
组合查询
- 创建组合查询(UNION)
mysql> (select ename,sal,deptno from emp where deptno in(10,20)) union (select ename,sal,deptno from emp where sal>=3000);
+--------+---------+--------+
| ename | sal | deptno |
+--------+---------+--------+
| SMITH | 800.00 | 20 |
| JONES | 3272.50 | 20 |
| CLARK | 2695.00 | 10 |
| SCOTT | 3000.00 | 20 |
| KING | 5000.00 | 10 |
| ADAMS | 1100.00 | 20 |
| FORD | 3000.00 | 20 |
| MILLER | 1300.00 | 10 |
| BLAKE | 3135.00 | 30 |
+--------+---------+--------+
9 rows in set
(0.00 sec)
- 包含或取消重复的行
在使用UNION时,重复的行被自动取消,若想放回所有行,使用UNION ALL而不是UNION。
- 对组合查询结果排序
SELECT语句的输出用ORDER BY子句排序。在用UNION组合查询时,只能使用一条ORDER BY子句,它必须出现在最后一条SELECT语句之后。对于结果集,不存在用一种方式排序一部分,而又用另一种方式排序另一部分的情况,因此不允许使用多条ORDER BY子句。
全文本搜索(第18章)
插入数据
- 插入完整行
- 插入多个行
- 插入检索出的数据
mysql>
insert into user(name)
select name
from user;
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql>
select *
from user;
+----+----------+
| id | name |
+----+----------+
| 11 | zhangsan |
| 13 | lisi |
| 14 | wangwu |
| 15 | zhangsan |
| 16 | lisi |
| 17 | wangwu |
+----+----------+
6 rows in set
(0.00 sec)
# INSERT SELECT中SELECT语句可包含WHERE子句以过滤插入的数据。
更新和删除数据
-
更新数据(UPDATE ... SET ... WHERE ...)
-
删除数据(DELETE FROM ... WHERE ...)
DELETE删除数据而不是表本身,TRUNCATE更快(TRUNCATE实际是删除原来的表并重新创建一个表,而不是逐行删除表中的数据)。
创建表和操纵表
- 创建表(CREATE)
- 更新表(ALERT)
mysql>
alter table user
add age VARCHAR(20);
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql>
select *
from user;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 王五 | NULL |
+----+--------+------+
1 row in set
(0.00 sec)
使用ALTER TABLE要极为小心,应该在进行改动前做一个完整的备份(模式和数据的备份)。数据
库表的更改不能撤销,如果增加了不需要的列,可能不能删除它们。类似地,如果删除了不应该删除的列,可能会丢失该列中的所有数据。
- 删除表(DROP)
- 重命名表(RENAME TABLE T1 to T2)
使用视图
- 视图
视图是虚拟的表。与包含数据的表不一样,视图只包含使用时动态检索数据的查询。
为什么使用视图?
- 重用SQL语句
- 简化复杂的SQL操作。在编写查询后,可以方便地重用它而不必知道它的基本查询细节。
- 使用表的组成部分而不是整个表。
- 保护数据。可以给用户授予表的特定部分的访问权限而不是整个表的访问权限。
- 更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据。
- 视图的规则和限制
下面是关于视图创建和使用的一些最常见的规则和限制。
- 与表一样,视图必须唯一命名(不能给视图取与别的视图或表相同的名字)。
- 对于可以创建的视图数目没有限制。
- 为了创建视图,必须具有足够的访问权限。这些限制通常由数据库管理人员授予。
- 视图可以嵌套,即可以利用从其他视图中检索数据的查询来构造一个视图。
- ORDER BY可以用在视图中,但如果从该视图检索数据SELECT中也含有ORDER BY,那么该视图中的ORDER BY将被覆盖。
- 视图不能索引,也不能有关联的触发器或默认值。
- 视图可以和表一起使用。例如,编写一条联结表和视图的SELECT语句。
- 使用视图(CREATE VIEW)
视图用CREATE VIEW语句来创建。
- 使用SHOW CREATE VIEW viewname;来查看创建视图的语句。
- 用DROP删除视图,其语法为DROP VIEW viewname;。
- 更新视图时,可以先用DROP再用CREATE,也可以直接用CREATE OR REPLACE VIEW。如果要更新的视图不存在,则第2条更新语句会创建一个视图;如果要更新的视图存在,则第2条更新语句会替换原
有视图。
- 利用视图简化复杂的联结
mysql>
create view empdept as
select ename, dname, sal
from emp
left join dept on emp.deptno = dept.deptno
where sal < 2000;
Query OK, 0 rows affected (0.04 sec)
mysql>
select *
from empdept;
+--------+------------+---------+
| ename | dname | sal |
+--------+------------+---------+
| SMITH | RESEARCH | 800.00 |
| ALLEN | SALES | 1600.00 |
| WARD | SALES | 1250.00 |
| MARTIN | SALES | 1250.00 |
| TURNER | SALES | 1500.00 |
| ADAMS | RESEARCH | 1100.00 |
| JAMES | SALES | 950.00 |
| MILLER | ACCOUNTING | 1300.00 |
+--------+------------+---------+
8 rows in set
(0.00 sec)
# 利用视图,可一次性编写基础的SQL,然后根据需要多次使用。
- 用视图重新格式化检索出的数据(Concat)
- 用视图过滤不想要的数据(WHERE子句)
- 使用视图与计算字段
- 更新视图
使用存储过程
- 存储过程
存储过程简单来说,就是为以后的使用而保存的一条或多条MySQL语句的集合。可将其视为批文件,虽然它们的作用不仅限于批处理。
使用存储过程有3个主要的好处,即简单、安全、高性能。
- 执行存储过程(CALL)
- 创建存储过程(CREATE PROCEDURE productpricing())
- 删除存储过程(DROP PROCEDURE productpricing)
- 使用参数
- 建立智能存储过程......
- 检查存储过程
使用游标
- 游标
不像多数DBMS, MySQL游标只能用于存储过程(和函数)。
使用触发器
管理事务处理
常用函数、脚本示例
查看数据库版本
select version();
查看数据库引擎信息
tips:MySQL存储引擎是基于表的,而不是库的。
show engines;
show variables like '%storage_engine%';
show table status from his where Name = 'zy_bh0';
计算年龄
select DATE_FORMAT(FROM_DAYS(DATEDIFF(now(), '1999-04-09')), '%Y') + 0 as age;
格式化当前时间
select DATE_FORMAT(now(), '%Y-%m-%d %H:%i:%s') as currentTime;
获取表结构信息
-- 获取表字段等其他信息
SELECT c.COLUMN_NAME `字段名称`,
c.COLUMN_TYPE `字段类型`,
c.CHARACTER_MAXIMUM_LENGTH `字段最大长度`,
IF(c.EXTRA = 'auto_increment',
CONCAT(c.COLUMN_KEY, '(', IF(c.EXTRA = 'auto_increment', '自增长', c.EXTRA), ')'),
c.COLUMN_KEY) `主外键`,
c.IS_NULLABLE `空标识`,
c.COLUMN_COMMENT `字段说明`
FROM information_schema.COLUMNS c
WHERE c.TABLE_SCHEMA = 'db_ms'
AND c.TABLE_NAME = 'user';
-- 获取表主键字段
SELECT k.COLUMN_NAME
FROM information_schema.table_constraints t
JOIN information_schema.key_column_usage k USING (constraint_name, table_schema, table_name)
WHERE t.constraint_type = 'PRIMARY KEY'
AND t.table_schema = 'db_ms'
AND t.table_name = 'user';
字段名称 | 字段类型 | 字段最大长度 | 主外键 | 空标识 | 字段说明 |
---|---|---|---|---|---|
create_at | timestamp | NULL | YES | ||
id | int | NULL | PRI(自增长) | NO | |
name | varchar(50) | 50 | YES | 用户名 | |
pwd | varchar(255) | 255 | YES | 密码 | |
update_at | timestamp | NULL | YES |
COLUMN_NAME |
---|
id |
修改库、表、表字段的字符编码集
-- 修改表字段
SELECT TABLE_SCHEMA '数据库',
TABLE_NAME '表',
COLUMN_NAME '字段',
CHARACTER_SET_NAME '原字符集',
COLLATION_NAME '原排序规则',
CONCAT('ALTER TABLE ', TABLE_SCHEMA, '.', TABLE_NAME, ' MODIFY COLUMN ', COLUMN_NAME, ' ', COLUMN_TYPE,
' CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;') '修正SQL'
FROM information_schema.`COLUMNS`
WHERE COLLATION_NAME RLIKE 'gb2312_chinese_ci';
-- 修改表
SELECT TABLE_SCHEMA '数据库',
TABLE_NAME '表',
TABLE_COLLATION '原排序规则',
CONCAT('ALTER TABLE ', TABLE_SCHEMA, '.', TABLE_NAME,
' COLLATE=utf8mb4_general_ci;') '修正SQL'
FROM information_schema.`TABLES`
WHERE TABLE_COLLATION RLIKE 'gb2312_chinese_ci';
-- 修改数据库
SELECT SCHEMA_NAME '数据库',
DEFAULT_CHARACTER_SET_NAME '原字符集',
DEFAULT_COLLATION_NAME '原排序规则',
CONCAT('ALTER DATABASE ', SCHEMA_NAME,
' CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;') '修正SQL'
FROM information_schema.`SCHEMATA`
WHERE SCHEMA_NAME = 'myemployees'
AND DEFAULT_CHARACTER_SET_NAME RLIKE 'gb2312';
操作XML格式字符串
MySQL分组后选出最大值所在的那一行数据
-- 方法一:
select a.*
from order_main a
inner join (select user_id, max(create_time) as create_time from order_main group by user_id) b
on a.user_id = b.user_id and a.create_time = b.create_time;
-- 方法二:
select a.*
from order_main a,
(select user_id, max(create_time) as create_time from order_main group by user_id) b
where a.user_id = b.user_id
and a.create_time = b.create_time;
MySQL查看唯一索引方式
select tab.table_id, tab.name, idx.name, idx.type
from information_schema.INNODB_SYS_TABLES tab
left outer join information_schema.INNODB_SYS_INDEXES idx on tab.table_id = idx.table_id and idx.type in (2)
where tab.name like 'his%'
and idx.NAME is not null
order by tab.name
;
工具类
正则校验工具类
/**
* @Author: DengJia
* @Date: 2022/5/10
* @Description: 正则校验工具类
*/
public class RegexUtil {
/**
* 移动手机号
*/
private static final Pattern MOBILE_RE = Pattern.compile("^[1][3,4,5,7,8][0-9]{9}$");
/**
* 带区号的电话
*/
private static final Pattern PHONE_WITH_AREA_CODE_RE = Pattern.compile("^[0][1-9]{2,3}-[0-9]{5,10}$");
/**
* 不带区号的电话
*/
private static final Pattern PHONE_WITHOUT_AREA_CODE_RE = Pattern.compile("^[1-9]{1}[0-9]{5,8}$");
/**
* 邮箱格式正则表达式
*/
private static final Pattern EMAIL_RE = Pattern.compile("^\\w+@(\\w+\\.)+\\w+$");
/**
* 身份证
*/
private static final Pattern ID_CARD_RE = Pattern.compile("^\\d{15}|\\d{17}[\\dxX]$");
/**
* 校验联系(手机、电话)号码是否合法
*
* @param number 号码
*/
public static void validatePhoneNumber(String number) {
if (!(isPhone(number) || isMobile(number))) {
throw new RuntimeException("号码校验失败,请输入合法格式!");
}
}
/**
* 校验电子邮箱是否合法
*
* @param email 电子邮箱
*/
public static void validateEmail(String email) {
if (!email.matches(EMAIL_RE.pattern())) {
throw new RuntimeException("邮箱校验失败,请输入合法格式!");
}
}
/**
* 校验身份证是否合法
*
* @param idCard 身份证
*/
public static void validateIdCard(String idCard) {
if (!idCard.matches(ID_CARD_RE.pattern())) {
throw new RuntimeException("身份证校验失败,请输入合法格式!");
}
}
/**
* 校验身份证与入参年龄、性别是否符合
*
* @param idCard 身份证
* @param age 年龄
* @param gender 性别 1男 2女
*/
public static void validatePersonalInfo(String idCard, Integer age, Integer gender) {
validateIdCard(idCard);
// 校验年龄
if (age != null) {
Calendar cal = Calendar.getInstance();
int nowYear = cal.get(Calendar.YEAR), nowMonth = cal.get(Calendar.MONTH) + 1, nowDay = cal.get(Calendar.DATE), year = Integer.parseInt(idCard.substring(6, 10)), month = Integer.parseInt(idCard.substring(10, 12)), day = Integer.parseInt(idCard.substring(12, 14));
int realAge;
boolean reach = (month < nowMonth) || (month == nowMonth && day <= nowDay);
if (reach) {
realAge = nowYear - year;
} else {
realAge = nowYear - year - 1;
}
if (!Objects.equals(age, realAge)) {
throw new RuntimeException("年龄与身份证不符,请检查!");
}
}
// 校验性别
if (gender != null) {
int flag = new Integer(idCard.substring(16, 17)) % 2;
boolean match = flag == 1 ? 1 == gender : 2 == gender;
if (!match) {
throw new RuntimeException("性别与身份证不符,请检查!");
}
}
}
public static void validatePersonalInfo(String idCard, String age, String gender) {
validatePersonalInfo(idCard, Integer.parseInt(age), Integer.parseInt(gender));
}
private static boolean isMobile(final String number) {
// 手机号验证
Matcher matcher = MOBILE_RE.matcher(number);
return matcher.matches();
}
private static boolean isPhone(final String number) {
// 电话号码验证
Matcher matcher;
if (number.length() > 9) {
matcher = PHONE_WITH_AREA_CODE_RE.matcher(number);
} else {
matcher = PHONE_WITHOUT_AREA_CODE_RE.matcher(number);
}
return matcher.matches();
}
}
集合元素求异
import java.util.*;
/**
* @Author: DengJia
* @Date: 2021/9/24 15:07
* @Description: 获取两个集合中不同的数据
*/
public class GetGatherDiff {
public static void main(String[] args) {
String[] a = {null, "21", "0", "39.42", "1013.34", "39.42", "3", "4.61", "14", "56", "56", "3", "9.22", "337.78", "0", "4", "18", "38", "7", "7", "9", "8", "19", "50", "16", "2", "3.6", "1.36", "75", "4", "26", "9.22", "3", "39.42", "337.78", "26", "50", "7", "7", "56", "9", "4", "56", "19", "18", "38", "7", "56", "19", "18", "56", "38", "7", "9", "4", "56", "1", "9.6", "10", "236.52", "6.57", "80.99", "2.7", "0", "0", "0", "7", "5", "21", "2.7", "80.99", "7", "0", "0", "0", "5", "21", "35.26", "3", "337.78", "9.22", "2.7", "80.99", "50", "0", "0", "0", "7", "5", "21", "80.99", "2.7", "10", "2.7", "0", "0", "0", "7", "5", "21", "2.7", "80.99", "2.7", "10", "0", "0", "0", "7", "5", "21", "2.7", "10", "2.7", "80.99", "0", "0", "7", "0", "21", "5", "2.7", "80.99", "2.7", "10", "26", "7", "0", "0", "0", "40.49", "2.7", "40.49", "10", "2.7", "2.7", "21", "13", "4", "0", "0", "7", "0"};
String[] b = {null, "21", "0", "39.42", "1013.34", "39.42", "4.61", "3", "14", "56", "56", "9.22", "3", "337.78", "4", "0", "8", "19", "18", "7", "9", "38", "7", "50", "3.6", "1.36", "16", "2", "75", "4", "26", "3", "39.42", "9.22", "337.78", "7", "9", "56", "38", "7", "56", "4", "19", "18", "26", "50", "56", "7", "19", "18", "9", "56", "38", "7", "4", "56", "1", "9.6", "10", "236.52", "6.57", "2.7", "80.99", "0", "0", "7", "0", "21", "5", "80.99", "2.7", "0", "0", "0", "7", "5", "21", "35.26", "3", "9.22", "337.78", "80.99", "2.7", "50", "0", "0", "0", "7", "5", "21", "2.7", "2.7", "80.99", "10", "0", "0", "7", "0", "21", "5", "10", "2.7", "2.7", "80.99", "0", "0", "0", "7", "5", "21", "2.7", "2.7", "80.99", "10", "7", "0", "0", "0", "21", "5", "10", "2.7", "2.7", "80.99", "26", "0", "0", "0", "7", "2.7", "40.5", "2.7", "40.5", "10", "2.7", "21", "4", "13", "7", "0", "0", "0"};
List<String> la = Arrays.asList(a);
List<String> lb = Arrays.asList(b);
System.out.println("disjoint = " + Collections.disjoint(la, lb));
System.out.println("different = " + getDifferent(la, lb) + getDifferent(lb, la));
}
public static List<String> getDifferent(List<String> l1, List<String> l2) {
Map<String, Integer> map = new HashMap<>(l1.size(), l2.size());
List<String> diff = new ArrayList<>();
List<String> maxList = l1;
List<String> minList = l2;
if (l2.size() > l1.size()) {
maxList = l2;
minList = l1;
}
for (String string : maxList) {
map.put(string, 1);
}
for (String string : minList) {
Integer cc = map.get(string);
if (cc != null) {
map.put(string, ++cc);
continue;
}
map.put(string, 1);
}
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 1) {
diff.add(entry.getKey());
}
}
return diff;
}
}
disjoint = false
different = [40.49][40.5]
属性非空校验类
public class Validate {
public static final String ALL_FIELDS = "ALL";
public static final String INCLUDE_FIELDS = "INCLUDE";
public static final String EXCLUDE_FIELDS = "EXCLUDE";
/**
* 实体属性非空校验
*
* @param obj 实体
* @param checkAll 校验所有属性
*/
public static void validate(Object obj, String checkAll) {
Field[] fields = obj.getClass().getDeclaredFields();
List<String> keySet = Arrays.stream(fields).map(Field::getName).collect(Collectors.toList());
if (ALL_FIELDS.equalsIgnoreCase(checkAll)) {
validate(obj, keySet);
}
}
/**
* @param obj 实体
* @param checkPart include or exclude
* @param part 待校验属性
*/
public static void validate(Object obj, String checkPart, Collection<String> part) {
Field[] fields = obj.getClass().getDeclaredFields();
if (INCLUDE_FIELDS.equalsIgnoreCase(checkPart)) {
validate(obj, part);
}
if (EXCLUDE_FIELDS.equalsIgnoreCase(checkPart)) {
List<String> check = Arrays.stream(fields).map(Field::getName).filter(f -> !part.contains(f)).collect(Collectors.toList());
validate(obj, check);
}
}
/**
* @param map 校验map
* @param checkSet 待校验key
*/
public static void validate(Map<String, Object> map, Collection<String> checkSet) {
List<String> notNull = new ArrayList<>();
for (String check : checkSet) {
for (String key : map.keySet()) {
if (!check.equals(key)) {
continue;
}
String value = map.get(check) == null ? "" : String.valueOf(map.get(check));
if (value == null || "".equals(value)) {
notNull.add(key);
}
break;
}
}
List<String> notAppear = checkSet.stream().filter(k -> !map.containsKey(k)).collect(Collectors.toList());
notNull.addAll(notAppear);
if (!notNull.isEmpty()) {
throw new RuntimeException("参数 " + notNull + " 不可为空!");
}
}
private static void validate(Object obj, Collection<String> checkSet) {
Field[] fields = obj.getClass().getDeclaredFields();
List<String> notNull = new ArrayList<>();
try {
for (String check : checkSet) {
for (Field field : fields) {
// 打开私有属性,否则只能获取到共有属性
field.setAccessible(true);
String name = field.getName();
if (!check.equals(name)) {
continue;
}
String value = field.get(obj) == null ? "" : String.valueOf(field.get(obj));
if (value == null || "".equals(value)) {
notNull.add(name);
}
break;
}
}
if (!notNull.isEmpty()) {
throw new RuntimeException("参数 " + notNull + " 不可为空!");
}
} catch (IllegalAccessException e) {
throw new RuntimeException("校验失败,异常堆栈信息:" + e);
}
}
}
接口统一返回类
package top.dj.entity;
import org.springframework.http.HttpStatus;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: DengJia
* @Date: 2021/9/27 15:30
* @Description: 通用返回
*/
public class R extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
public R() {
put("code", 0);
put("msg", "success");
}
public static R error() {
return error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "未知异常,请联系管理员!");
}
public static R error(String msg) {
return error(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg);
}
public static R error(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R errorData(Object obj) {
R r = new R();
r.put("errorData", obj);
return r;
}
public static R ok() {
return new R();
}
public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
return r;
}
public static R ok(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R ok(Map<String, Object> map) {
R r = new R();
r.putAll(map);
return r;
}
public static R data(Object obj) {
R r = new R();
r.put("data", obj);
return r;
}
public static R data(String msg, Object obj) {
R r = new R();
r.put("msg", msg);
r.put("data", obj);
return r;
}
@Override
public Object put(String key, Object value) {
super.put(key, value);
return this;
}
public Object getCode() {
return super.get("code");
}
}
JSON转换工具类(GSON实现)
import java.lang.reflect.Type;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* description:
* json转化工具类 -- 使用gson实现
* gson为线程安全的类,所以可以使用工具类来减少对象的实例化
*
* <dependency>
* <groupId>com.google.code.gson</groupId>
* <artifactId>gson</artifactId>
* <version>2.4</version>
* </dependency>
*
* @author wkGui
*/
//TODO 删除
public class GsonUtil {
private static Gson filterNullGson;
private static Gson nullableGson;
static {
nullableGson = new GsonBuilder()
.enableComplexMapKeySerialization()
.serializeNulls()
.setDateFormat("yyyy-MM-dd HH:mm:ss:SSS")
.create();
filterNullGson = new GsonBuilder()
.enableComplexMapKeySerialization()
.setDateFormat("yyyy-MM-dd HH:mm:ss:SSS")
.create();
}
protected GsonUtil() {
}
/**
* 根据对象返回json 不过滤空值字段
*/
public static String toJsonWtihNullField(Object obj) {
return nullableGson.toJson(obj);
}
/**
* 根据对象返回json 过滤空值字段
*/
public static String toJsonFilterNullField(Object obj) {
return filterNullGson.toJson(obj);
}
/**
* 将json转化为对应的实体对象
* new TypeToken<HashMap<String, Object>>(){}.getType()
*/
public static <T> T fromJson(String json, Type type) {
return nullableGson.fromJson(json, type);
}
}
Java字符串反转
/**
* 递归实现字符串的反转
*
* @param str 待反转的字符串
* @return 反转后的字符串
*/
public static String recursionReverse(String str) {
if (str == null || str.length() <= 1) {
return str;
}
return recursionReverse(str.substring(1)) + str.charAt(0);
}
public static String stringReverse(String str) {
StringBuilder builder = new StringBuilder();
for (int i = str.length() - 1; i >= 0; i--) {
builder.append(str.charAt(i));
}
return builder.toString();
}
排序
冒泡排序
public class SortingAlgorithm {
public static void main(String[] args) throws Exception {
int[] array = {4, 2, 3, 5, 1, 7, 6};
bubbleSortImprove(array);
}
/*
* 冒泡排序
*
* - 时间复杂度 O(N^2);
* - 稳定性,冒泡排序是稳定的算法;
*/
/**
* 冒泡排序
*
* @param array 待排序的数组
*/
public static void bubbleSort(int[] array) {
int l = array.length;
for (int i = l - 1; i > 0; i--) {
// 将 array[0...i] 中最大的数据放在末尾
for (int j = 0; j < i; j++) {
if (array[j] > array[j + 1]) {
// 交换array[j] 和 array[j + 1]
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
System.out.println(Arrays.toString(array));
}
/**
* 冒泡排序(改进版),若某一次循环未发生交换,说明该数组已经有序。
*
* @param array 待排序的数组
*/
public static void bubbleSortImprove(int[] array) {
int l = array.length;
boolean flag;
for (int i = l - 1; i > 0; i--) {
// 初始化标记
flag = true;
// 将 array[0...i] 中最大的数据放在末尾
for (int j = 0; j < i; j++) {
if (array[j] > array[j + 1]) {
// 交换array[j] 和 array[j + 1]
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
// 发生交换,置标记为false
flag = false;
}
}
if (flag) {
break;
}
}
System.out.println(Arrays.toString(array));
}
}
快速排序
public class SortingAlgorithm {
public static void main(String[] args) throws Exception {
int[] array = {7, 6, 5, 4, 3, 2, 1};
quickSort(array, 0, array.length - 1);
System.out.println(Arrays.toString(array));
}
/*
* 快速排序(使用了分治法策略)
* - 基本流程
* - 从数列中挑出一个基准值;
* - 将所有比基准值小的摆放在基准前面, 所有比基准值大的摆在基准的后面;
* - 递归地把 "基准值前面的子数列" 和 "基准值后面的子数列" 进行排序.
* - 时间复杂度
* - 最坏 O(N^2)
* - 平均 O(N*lgN)
* - 稳定性,快速排序是不稳定的算法;
*/
/**
* 快速排序
*
* @param array -- 待排序的数组
* @param left -- 数组的左边界(例如,从起始位置开始排序,则left = 0)
* @param right -- 数组的右边界(例如,排序截至到数组末尾,则right = array.length - 1)
*/
public static void quickSort(int[] array, int left, int right) {
if (left < right) {
int i, j, tmp;
i = left;
j = right;
tmp = array[i];
while (i < j) {
while (i < j && array[j] > tmp) {
// 从右向左找第一个小于tmp的数
j--;
}
if (i < j) {
array[i++] = array[j];
}
while (i < j && array[i] < tmp) {
// 从左向右找第一个大于tmp的数
i++;
}
if (i < j) {
array[j--] = array[i];
}
}
array[i] = tmp;
// 递归调用
quickSort(array, left, i - 1);
// 递归调用
quickSort(array, i + 1, right);
}
}
}
直接插入排序
public class SortingAlgorithm {
public static void main(String[] args) throws Exception {
straightInsertionSort(new int[]{7, 6, 5, 4, 3, 2, 1});
}
/*
* 直接插入排序
* - 基本流程
* - 把n个待排序的元素看成为一个有序表和一个无序表;
* - 开始时有序表中只包含1个元素,无序表中包含有n-1个元素;
* - 排序过程中每次从无序表中取出第一个元素;
* - 将它插入到有序表中的适当位置,使之成为新的有序表;
* - 重复n-1次可完成排序过程;
* - 时间复杂度
* - 最坏 O(N^2)
* - 稳定性,直接插入排序是稳定的算法;
*/
/**
* 直接插入排序
*
* @param array 待排序的数组
*/
public static void straightInsertionSort(int[] array) {
int i, j, k;
int l = array.length;
for (i = 1; i < l; i++) {
// 为array[i]在前面的array[0...i-1]有序区间中找一个合适的位置
for (j = i - 1; j >= 0; j--) {
if (array[j] < array[i]) {
break;
}
}
// 若找到一个合适的位置
if (j != i - 1) {
// 将比array[i]大的数往后移
int tmp = array[i];
for (k = i - 1; k > j; k--) {
array[k + 1] = array[k];
}
// 将array[i]放在正确的位置上
array[k + 1] = tmp;
}
}
System.out.println(Arrays.toString(array));
}
}