[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bx8wW2iS-1680618008606)(./assets/XuanZiShare_QQ_3336392096.jpg)]
前言:
此文为玄子,学习 BCSP 一二期后整理的文章,文中对知识点的解释仅为个人理解,源码均可直接复制运行
配套PPT,站点源码,等学习资料请加文章封面联系方式
针对课程的一些调整:
- 如果你当前正在学习
BCSP S1
课程的C sharp语言和SQL Server数据库技术
并不影响阅读浏览这篇文章,SQL Server
与MySQL
的绝大部分SQL语法都是相通的,关于SQL语法我均写了详细的介绍- 一期
C sharp语言和SQL Server数据库技术
与二期JAVA开发之JAVA数据库编程
这两本书的相似度较高,所以针对数据库编程,只写了二期的JAVA开发之JAVA数据库编程
- 这篇文章与之前的
玄子Share-BCSP助学手册之JAVA开发
以及玄子Share-BCSP助学手册之前端开发
属于同一系列Java开发之Java数据库编程
这本书所学,几乎就是以后工作的日常操作,所以这篇文章,会加入更多关于我对数据库及JDBC的理解,相对前两篇文章,会比较啰嗦,请谅解- 文章演示的数据库为BCSP配套数据库
hospital
文章第八章附件有源文件,可直接复制运行- 文章的部分知识相对课程有增改,以课本为准
- 课程中部分软件涉及版权无法展示安装教程,可加封面联系方式获取软件和安装教程
按照教材上的要求,统一使用MySQL版本号为5.7.40
的版本
MySQL 的安装方式有两种
安装器安装,快速便捷,安装过程均为图像操作界面,但缺点是卸载,极其麻烦,不仅要删除MySQL文件,还要删除注册表,等相关信息。如果卸载不干净,影响以后的安装配置等。
压缩包安装,相对比较麻烦,需要解压后,自行配置MySQL服务等,但优点是,卸载非常简单,只需要把解压文件删除即可。
我推荐使用,压缩包解压配置的方式安装MySQL,虽然麻烦了一点,但后期使用MySQL出现问题,需要重新安装MySQL就会很舒服。
两种安装方式我都写有教程,不用纠结使用哪种安装方式,能安装上就行了。
官方 https://downloads.mysql.com/archives/community/
度盘 https://pan.baidu.com/s/1fuGdLIDXx2-2b5mEdqEh3g?pwd=xzsr
注意选择版本号为5.7.40
下载完后我们会得到一个以 MySQL 开头命名的压缩包,我们需要把这个压缩包解压到本地
这里可以把 MySQL 文件名后面的-winx64
删除掉(度盘下载的压缩包我已经删除过了)
推荐将文件解压到 C 盘的 Program Files 文件下,即解压路径为C:\Program Files\mysql-5.7.40
在C:\Program Files\mysql-5.7.40
文件夹内新建一个文本文档,【强制】命名为:my.ini
注意开启显示文件拓展名
设置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BGGSYu1O-1680618008609)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230202161947.png)]
如果在度盘下载的压缩包,我在压缩包中已经写入过
my.ini
文件,只需要注意自己的安装路径是否和配置文件内路径一致即可
使用记事本方法打开my.ini
文件,将下面的配置文件粘贴进去
[mysqld]
#设置3306端口
port = 3306
# 设置mysql的安装目录
basedir=C:\Program Files\mysql-5.7.40
# 设置mysql数据库的数据的存放目录
datadir=C:\Program Files\mysql-5.7.40\data
# 允许最大连接数
max_connections=200
# 服务端使用的字符集默认为8比特编码的latin1字符集
character-set-server=utf8
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
#跳过密码
skip-grant-tables
basedir:就是mysql的安装路径
datadir:只需要把
\data
前面的路径替换即可文本里的代码前面是没有数字的
Path
双击进入bin
目录(实际路径以自己安装时设置为准)C:\Program Files\mysql-5.7.40\bin
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DRPb2ri3-1680618008611)(./assets/image-20230202162835333.png)]
Win + R
然后通过管理员身份打开cmd
mysql --version
查看环境是否配置成功[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6GByu5q6-1680618008612)(./assets/image-20230202163232310.png)]
Win + R
打开运行输入cmd
以管理员身份运行cd /d C:\Program Files\mysql-5.7.40\bin
mysqld -install
命令安装 MySQL 服务mysqld --initialize-insecure
初始化 data 目录net start mysql
启动 MySQL 服务mysql -uroot
登录 MySQLupdate mysql.user set authentication_string=password('root') where user='root' and Host = 'localhost';
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jkya4ICN-1680618008612)(./assets/image-20230202164124547.png)]
root
就是登录密码,账户名也是root
或者你可以把密码改成自己喜欢的flush privileges;
刷新一下权限exit
退出 MySQL 即可退出后回到my.ini
配置文件中,用#
注释掉或者直接删除掉最后一行代码skip-grant-tables
然后保存退出
到这里就已经成功下载并配置好了 MySQL
命令行先输入net stop mysql
关闭 MySQL 服务
在输入net start mysql
开启 MySQL 服务
这里报错的请自己到服务中手动打开 MySQL 服务后重新尝试
登录数据库
mysql -uroot -proot
mysql -u账户名 -p密码
注意密码前后不能有空格
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jHpcOA8F-1680618008613)(./assets/image-20230202165147101.png)]
最后可以输入一个查询版本信息的SQl语句玩一下:
select version(),user();
代码后面有分号
MySQL 压缩包安装教程
官方 https://downloads.mysql.com/archives/installer/
度盘 https://pan.baidu.com/s/1fuGdLIDXx2-2b5mEdqEh3g?pwd=xzsr
注意选择版本号为5.7.40
下载完后我们会得到一个以 MySQL 开头命名的msi
安装器,直接双击运行安装
NO
即可Custom
自定义安装MySQL Server 5.7.40 - X64
点击箭头添加到右边Execute
执行安装即可MySQL
登录密码(默认设置为 root
)MySQL57
就是服务器名称,点击Next
下一步Path
双击进入bin
目录(实际路径以自己安装时设置为准)C:\Program Files\MySQL\MySQL Server 5.7\bin
Win + R
然后通过管理员身份打开cmd
mysql --version
查看环境是否配置成功[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nUFVwqB7-1680618008617)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230330223945.png)]
Win + R
打开cmd
窗口mysql -uroot -proot
(-u后的root
为管理员账号,-p后的root
为之前设置的MySQL登录密码,没有空格直接跟密码)最后可以输入一个查询版本信息的SQl语句玩一下:
select version(),user();
代码后面有分号
MySQL 安装器安装教程
压缩包安装方式安装的MySQL,卸载就直接删除掉解压后的MySQL文件夹即可,然后删除配置的 MySQL 环境变量
Win + R
打开cmd
窗口输入services.msc
打开服务到这里只是卸载了MySQL软件,MySQL还有许多文件并没有删掉,如果不删除干净,会影响后续安装导致安装失败。
C:\Program Files (x86)\MySQL
内所有文件C:\ProgramData\MySQL
内所有文件,该文件是默认隐藏的,设置显示后即可见C:\Documents and Settings\All Users\Application Data\MySQL
内所有文件到这里也只是删掉了MySQL所有残余文件,还需要删除MySQL的注册表信息
Win + R
打开cmd
窗口输入regedit
打开注册表HKEY_LOCAL_MACHINE/SYSTEM/ControlSet001/Services/Eventlog/Application/MySQL
HKEY_LOCAL_MACHINE/SYSTEM/ControlSet002/Services/Eventlog/Application/MySQL
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Eventlog/Application/MySQL
Navicate官网 https://www.navicat.com/en/products
SqlYog官网 https://sqlyog.en.softonic.com/
度盘 https://pan.baidu.com/s/1fuGdLIDXx2-2b5mEdqEh3g?pwd=xzsr
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-koVrGUFS-1680618008620)(./assets/image-20230205154315466-1680219755644-4.png)]
Navicate 涉及版权无法分享,加封面联系方式获取软件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zywy07Tj-1680618008621)(./assets/image-20230331164902240.png)]
SqlYog 涉及版权无法分享,加封面联系方式获取软件
PowerDesigner 涉及版权无法分享,加封面联系方式获取软件
良好的数据库设计
糟糕的数据库设计
数据库设计是对数据库中实体以及这些实体之间的关系进行规划和结构化的过程,当数据库比较复杂时,需要设计数据库
收集信息
标识实体 (Entity)
标识每个实体的属性(Attribute)
标识实体之间的关系(Relationship)
数据库设计中,不能重复出现含义相同的实体
E-R图也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型、属性和联系的方法,用来描述现实世界的概念模型
符号 | 含义 |
---|---|
□ | 实体,一般是名词 |
○ | 属性,一般是名词 |
♢ | 关系,一般是动词 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WfN24s22-1680618008621)(./assets/image-20230331234821123.png)]
概念数据模型设计
逻辑数据模型设计
物理数据模型设计
概念数据模型 | 逻辑数据模型 | 物理数据模型 |
---|---|---|
实体(Entity) | 实体(Entity) | 表(Table) |
属性(Attribute) | 属性(Attribute) | 列(Column) |
标识符(Identifier) | 标识符(Primary Identifier/ Foreign Identifier) | 键(Primary key/ Foreign key) |
关系(Relationship) | 关系(Relationship) | 参照完整性约束(Reference) |
基本转换原理
将E-R图中每一个实体转换成一个表,实体的属性转换为表的列,实体的标识符转换为表的主键
将实体关系转化为表之间的引用完整性约束
一般会各自转换为一张表,且后者对应的表引用前者对应的表
一个表中的主键对应另一个表中可重复的字段
主键的值是不能重复的,关联的字段是可以重复的
存在一个值对应一个值(一对一)或者一个值对应多个值(一对多)
推荐使用的版本:PowerDesigner 16.5
PowerDesigner 涉及版权无法分享,加封面联系方式获取软件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WQMQ5iat-1680618008622)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230401000958.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WOf2sAcG-1680618008622)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230401001019.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wL0uYqUy-1680618008623)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230401001132.png)]
生成的 SQL文件可直接拿到 Navicate 运行
PowerDesigner中开发数据库模型图时,需要指定该模型图是为数据库管理系统开发的,因为PowerDesigner支持用数据库模型图生成数据库创建脚本的功能
为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则
第一范式
第二范式
第三范式
为满足某种商业目标,数据库性能比规范化数据库更重要
通过在给定的表中添加额外的字段,以大量减少需要从中搜索信息所需的时间
通过在给定的表中插入计算列(如成绩总分),以方便查询
在数据规范化同时,要综合考虑数据库的性能
名称 | 商品型号 | 单价 | 数量 | 金额 |
---|---|---|---|---|
电视机 | 29英寸 | 2500 | 40 | 100000 |
金额 = 单价 * 数量,是冗余的列
为了提高查询统计速度,以空间换取时间
不要轻易违反数据库设计的规范化原则,如果处理不好,可能会适得其反,使应用程序运行速度更慢
向各表中插入数据,查看表中的每个属性列是否存在重复、插入异常、更新异常和删除异常
对照三大范式的定义,解决表中的异常问题
第一范式的目标是确保每列都是不可再分的最小数据单元,查看每列是否都满足第一范式
第二范式的每列与主键相关,不相关的放入别的表中,即要求一个表只描述一件事情
第三范式要求表中各列必须和主键直接相关,不能间接相关,查看各表,满足第三范式,对于不满足三大范式的表要进行拆分
MySQL 是一款流行的开源数据库,也是一个关系型数据库管理系统
在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一
时间 | 里程碑 |
---|---|
1996 年 | MySQL 1.0 发布。它的历史可以追溯到 1979 年,作者 Monty 用 BASIC 设计的一个报表工具 |
1996 年 | 10 月 3.11.1 发布。MySQL 没有 2.x 版本 |
2000 年 | ISAM 升级成 MyISAM 引擎。MySQL 开源 |
2003 年 | MySQL 4.0 发布,集成 InnoDB 存储引擎 |
2005 年 | MySQL 5.0 版本发布,提供了视图、存储过程等功能 |
2008 年 | MySQL AB 公司被 Sun 公司收购,进入 Sun MySQL 时代 |
2009 年 | Oracle 收购 Sun 公司,进入 Oracle MySQL 时代 |
2010 年 | MySQL 5.5 发布,InnoDB 成为默认的存储引擎 |
2016 年 | MySQL 发布 8.0.0 版本 |
为什么没有 MySQL6、MySQL7?
MySQL5.6 可以当成 MySQL6.x
MySQL5.7 可以当成 MySQL7.x
运行速度快
使用成本低
容易使用
可移植性强
适用用户广
丰富的接口
支持查询语言
安全性和连接性
安全性和连接性; 十分灵活和安全的权限和密码系统,允许主机验证。连接到服务器时,所有的密码均采用加密形式,从而保证了密码安全
并且由于MySQL是网络化的,因此可以在因特网网上的任何地方访问,提高数据共享效率
官网 https://downloads.mysql.com/archives/community/
CSDN安装教程 https://blog.csdn.net/qq_62283694/article/details/129869634
安装教程见第一章、课程软件的安装与卸载
首先检查 MySQL 服务是否启动
电脑按下Win + R 输入 cmd
打开dos
窗口
net start mysql
net stop mysql
mysql –h服务器主机地址 –u用户名 –p密码
参数说明:
-h
指定客户端所要登录的 MySQL 主机名, 登录本机(localhost 或 127.0.0.1)该参数可以省略
-u
登录的用户名
-p
告诉服务器将会使用一个密码来登录, 如果所要登录的用户名密码为空, 可以忽略此选项
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iHalnaYf-1680618008623)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230205152901.png)]
SELECT VERSION(),USER();
分号为结束符,不可缺
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D13ahSVO-1680618008624)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230205152928.png)]
CREATE DATABASE 数据库名;
Query OK:SQL语句执行成功
1 row affected:操作影响的行数
(0.00 sec):操作执行时间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nuQ2pNPs-1680618008624)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230205153046.png)]
SHOW databases;
student:用户数据库
其他的是系统数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ARXmDuEx-1680618008624)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230205153258.png)]
USE 数据库名;
使用数据表前,必须先选择该数据库!
ERROR 1049 (42000): Unknown database 'a'
数据库不存在,报错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XrsimpSZ-1680618008624)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230205153925.png)]
DROP DATABASE 数据库名;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B4fKjD7o-1680618008625)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230205154227.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cuodmnhH-1680618008625)(./assets/image-20230205154315466-1680219755644-4.png)]
Navicat Premium是一款功能强大的、可支持多连接的数据库管理工具
允许在单一程序中同时连接多达7种数据库
MySQL
MariaDB
MongoDB
SQL Server
SQLite
Oracle
PostgreSQL
官网 https://www.navicat.com/en/products
推荐使用:Navicat Premium 16 最新版
Navicate 涉及版权无法分享,加封面联系方式获取软件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pj0DjFUJ-1680618008626)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230205154815-1680247296828-4.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7XjhpjEs-1680618008626)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230205160159-1680247296828-6.png)]
utf8
点击 SQL 预览可以查看建表语句[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PdAVy0rE-1680618008626)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230205160210-1680247296828-7.png)]
操作数据库中所包含的数据
创建和删除数据库对象等操作
对数据库中的数据进行查询
控制数据库组件的存取许可、存取权限等
MySQL 中定义数据字段的类型对你数据库的优化是非常重要的
MySQL 支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型
类型 | 说明 | 取值范围 | 存储需求 |
---|---|---|---|
TINYINT | 非常小的数据 | 有符值:-27~27-1 无符号值:0~28-1 |
1字节 |
SMALLINT | 较小的数据 | 有符值:-215~215-1 无符号值:0~216-1 |
2字节 |
MEDIUMINT | 中等大小的数据 | 有符值:-223~223-1 无符号值:0~224-1 |
3字节 |
INT | 标准整数 | 有符值:-231~231-1 无符号值:0~232-1 |
4字节 |
BIGINT | 较大的整数 | 有符值:-263~263-1 无符号值:0~264-1 |
8字节 |
FLOAT | 单精度浮点数 | ±1.1754351e-38 | 4字节 |
DOUBLE | 双精度浮点数 | ±2.2250738585072014e-308 | 8字节 |
DECIMAL | 字符串形式的浮点数 | Decimal(M,D) | M+2个字节 |
**UNSIGNED 属性:**标识为无符号数,非负数
**ZEROFILL 属性:**宽度(位数)不足以0填充
若某数值字段指定了 ZEROFILL 属性,将自动添加 UNSIGNED 属性
数值位数小于宽度,以0填充
数值位数大于宽度,显示实际位数
日期类型 | 格式 | 取值范围 | 用途 |
---|---|---|---|
DATE | YYYY-MM-DD | 1000-01-01 / 9999-12-31 | 日期值 |
TIME | HH:MM:SS | -835:59:59 / 838:59:59 | 时间值或持续时间 |
YEAR | YYYY | 1901~2155 | 年份值 |
DATETIME | YYYY-MM-DD hh:mm:ss | 1000-01-01 00:00:00 到 9999-12-31 23:59:59 | 混合日期和时间值 |
TIMESTAMP | YYYY-MM-DD hh:mm:ss | ‘1970-01-01 00:00:01’ UTC 到 ‘2038-01-19 03:14:07’ UTC 结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07 |
混合日期和时间值,时间戳 |
若某日期字段默认值为系统当前日期,可指定为
TIMESTAMP
类型
字符串类型 | 大小 | 用途 |
---|---|---|
CHAR[(M)] | 0-255 bytes | 定长字符串 |
VARCHAR[(M)] | 0-65535 bytes | 变长字符串 |
TINYBLOB | 0-255 bytes | 不超过 255 个字符的二进制字符串 |
TINYTEXT | 0-255 bytes | 短文本字符串 |
BLOB | 0-65 535 bytes | 二进制形式的长文本数据 |
TEXT | 0-65 535 bytes | 长文本数据 |
MEDIUMBLOB | 0-16 777 215 bytes | 二进制形式的中等长度文本数据 |
MEDIUMTEXT | 0-16 777 215 bytes | 中等长度文本数据 |
LONGBLOB | 0-4 294 967 295 bytes | 二进制形式的极大文本数据 |
LONGTEXT | 0-4 294 967 295 bytes | 极大文本数据 |
char(m) 和 varchar(m) 中括号中 m 代表字符的个数,并不代表字节个数
比如 CHAR(30) 就可以存储 30 个字符
CHAR 和 VARCHAR 类型类似,但它们保存和检索的方式不同。它们的最大长度和是否尾部空格被保留等方面也不同。在存储或检索过程中不进行大小写转换
BINARY 和 VARBINARY 类似于 CHAR 和 VARCHAR,不同的是它们包含二进制字符串而不要非二进制字符串。也就是说,它们包含字节字符串而不是字符字符串。这说明它们没有字符集,并且排序和比较基于列值字节的数值值
BLOB 是一个二进制大对象,可以容纳可变数量的数据。有 4 种 BLOB 类型:TINYBLOB、BLOB、MEDIUMBLOB 和 LONGBLOB。它们区别在于可容纳存储范围不同
有 4 种 TEXT 类型:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。对应的这 4 种 BLOB 类型,可存储的最大长度不同,可根据实际情况选择
CREATE TABLE table_name (column_name column_type);
CREATE TABLE [IF NOT EXISTS] 表名 (
字段 1 数据类型 [ 字段属性 | 约束 ][ 索引 ][ 字段备注 ],
字段 2 数据类型 [ 字段属性 | 约束 ][ 索引 ][ 字段备注 ],
字段 n 数据类型 [ 字段属性 | 约束 ][ 索引 ][ 字段备注 ]
)[ 表类型 ][ 表字符集 ][ 表备注 ];
IF NOT EXISTS 判断表是否存在,存在就不重复建表了
多字段使用逗号分隔,保留字用撇号括起来
演示案例
CREATE TABLE IF NOT EXISTS xuanzi(
id INT UNSIGNED AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
phone VARCHAR(40) NOT NULL,
date DATE,
PRIMARY KEY ( id )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
- 如果你不想字段为
NULL
可以设置字段的属性为NOT NULL
,在操作数据库时如果输入该字段的数据为NULL
,就会报错- AUTO_INCREMENT 定义列为自增的属性,一般用于主键,数值会自动加1
- PRIMARY KEY 关键字用于定义列为主键。 您可以使用多列来定义主键,列间以逗号分隔
- ENGINE 设置存储引擎,CHARSET 设置编码
ALTER TABLE <旧表名> RENAME [TO] <新表名>;
# 修改表名称
ALTER TABLE XuanZi RENAME TO patient;
ALTER TABLE 表名 ADD 字段名 数据类型 [属性];
# 添加字段
ALTER TABLE patient add XuanZi VARCHAR(20) NOT NULL;
ALTER TABLE 表名 CHANGE 原字段名 新字段名 数据类型 [属性];
#修改字段
ALTER TABLE patient CHANGE XuanZiemail email VARCHAR(20);
ALTER TABLE 表名 DROP 字段名;
# 删除字段
ALTER TABLE patient DROP XuanZi;
ALTER TABLE 表名 ADD CONSTRAINT 约束名称 PRIMARY KEY 表名(主键字段);
# 添加主键约束 添加约束 约束名称 约束类型 表(字段)
ALTER TABLE patient ADD CONSTRAINT pk_patientID PRIMARY KEY patient(patientID);
ALTER TABLE 表名 ADD CONSTRAINT 约束名称 FOREIGN KEY(外键字段)REFERENCES 关联表名(关联字段);
# 添加外键约束 添加约束 约束名称 约束类型 (添加外键的字段) 关联 外键(字段)
ALTER TABLE prescription ADD CONSTRAINT fk_patientID_patient FOREIGN KEY (patientID) REFERENCES patient(patientID);
MySQL中,
MyISAM
存储类型的表不支持外键。因此,可以通过建立逻辑关联的方式保证数据的完整性和一致性
DROP TABLE [IF EXISTS] 表名;
在删除表之前,先使用
IF EXISTS
语句验证表是否存在,执行 DROP TABLE 的表是空表,则可以直接删除如果表中已存储了业务数据,则需和数据库管理员联系,先对数据库数据进行备份并确认,再执行删除表及数据的操作
# 单行注释
-- 单行注释
/**
多行注释
*/
名称 | 关键字 | 说明 |
---|---|---|
主键约束 | PRIMARY KEY(PK) | 设置该字段为表的主键 可唯一标识该表记录 |
外键约束 | FOREIGN KEY(FK) | 用于在两表之间建立关系, 需要指定引用主表的哪一字段 |
自动增长 | AUTO_INCREMENT | 设置该列为自增字段 默认每条自增1 通常用于设置主键 |
非空约束 | NOT NULL | 字段不允许为空 |
默认约束 | DEFAULT | 赋予某字段默认值 |
唯一约束 | UNIQUE KEY(UK) | 设置字段的值是唯一的 允许为空,但只能有一个空值 |
主键:使用数据库表中某一字段或某几个字段唯一标识所有记录
CREATE TABLE [IF NOT EXISTS] 表名 (
字段 1 数据类型 PRIMARY KEY,
#……
);
-- 定义字段的同时指定主键
CREATE TABLE [IF NOT EXISTS] 表名 (
字段 1 数据类型 ,
#……
[CONSTRAINT< 约束名 >] PRIMARY KEY[ 列名 ]
);
-- 定义所有字段之后指定主键
CREATE TABLE [IF NOT EXISTS] 表名 (
#……
PRIMARY KEY [字段1,字段2,…… ]
);
ALTER TABLE 表名 ADD CONSTRAINT 约束名称 FOREIGN KEY(外键字段)REFERENCES 关联表名(关联字段);
# 添加外键约束 添加约束 约束名称 约束类型 (添加外键的字段) 关联 外键(字段)
其他约束的设置方式同理
定义 | 作用 | 个数 | |
---|---|---|---|
主键 | 唯一标识一条记录,不能出现重复且不能出现空值 | 保证数据完整性 | 主键只能有一个 |
外键 | “从表”中对应于“主表”的列,在从表中称为外键或者引用键“主表”中的列应为主键或者唯一键 | 和其他表建立关联,保持数据的一致性 | 一个表可以有多个外键 |
COMMENT关键字
在表结构中可以看到,与代码注释不同、
CREATE TABLE test (
id int(11) UNSIGNED COMMENT '编号'
)COMMENT='测试表';
默认情况下,MySQL所有数据库、表、字段等使用 MySQL 默认字符集
为了存储独特的数据,可在创建表时指定字符集
CREATE TABLE [IF NOT EXISTS] 表名(
#……
)CHARSET = 字符集名;
CREATE TABLE `checkitem` (
`checkItemID` int(4) NOT NULL AUTO_INCREMENT COMMENT '检查项目编号',
`checkItemName` varchar(50) NOT NULL COMMENT '检查项目名称',
`checkItemCost` float NOT NULL COMMENT '检查项目价格',
PRIMARY KEY (`checkItemID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COMMENT = '检查项目表';
SHOW TABLES;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qJXYQ1Rd-1680618008626)(./assets/image-20230331173945642.png)]
DESCRIBE 表名;
DESC 表名;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uVITntrR-1680618008627)(./assets/image-20230331173500098.png)]
为了避免
DOS
窗口出现中文乱码,可执行SET NAMES gbk;
存储引擎,是存储数据的核心组件,指定了表的存储类型和存储形式
SHOW ENGINES;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pcg13h6A-1680618008627)(./assets/image-20230331232103064.png)]
InnoDB 存储引擎
MyISAM 存储引擎
功能 | InnoDB | MyISAM |
---|---|---|
事务处理 | 支持 | 不支持 |
外键约束 | 支持 | 不支持 |
表空间大小 | 较大 | 较小 |
数据行锁定 | 支持 | 不支持 |
MySQL 5.7 版本默认的存储引擎是InnoDB
SHOW VARIABLES LIKE 'default_storage_engine%';
-- default_storage_engine% 默认存储引擎
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YoeUH1S3-1680618008628)(./assets/image-20230331232525679.png)]
my.ini
配置文件default-storage-engine=MyISAM
-- MyISAM 改为其他类型的存储引擎
修改配置文件后,需要重启MySQL服务设置才会生效
数据表默认使用当前 MySQL 默认的存储引擎
创建表时,可以设置表的存储引擎类型
CREATE TABLE 表名 (
# ……
) ENGINE= 存储引擎 ;
演示案例
CREATE TABLE `checkitem` (
`checkItemID` int(4) NOT NULL AUTO_INCREMENT COMMENT '检查项目编号',
`checkItemName` varchar(50) NOT NULL COMMENT '检查项目名称',
`checkItemCost` float NOT NULL COMMENT '检查项目价格',
PRIMARY KEY (`checkItemID`) USING BTREE
) ENGINE = MyISAM;
MyISAM 类型表文件
*.frm
表结构定义文件
*.MYD
数据文件
*.MYI
索引文件
InnoDB 类型表文件
*.frm
表结构定义文件
ibdata1
文件
因操作系统而异,可查my.ini
datadir="C:\ProgramData\MySQL\MySQL Server 5.7\Data"
innodb_data_home_dir="D:/MySQL Datafiles/"
HELP 查询内容;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d4iem2aE-1680618008628)(./assets/image-20230331233452271.png)]
HELP contents;
#查看帮助文档目录列表
HELP Data Definition;
#查看数据定义
HELP CREATE DATABASE;
#查看CREATE DATABASE的帮助信息
DML(Data Manipulation Language)DML对数据库中表记录的执行操作
插入(INSERT)
插入单行数据
插入多行数据
将查询结果插入到新表
更新(UPDATE)
删除(DELETE)
软件开发人员日常使用最频繁的操作,必备技能!!!
INSERT INTO 表名 [(字段名列表)] VALUES (值列表);
# 增
INSERT into gradeinfo (grade) VALUES ('ACCP');
字段名是可选的,如省略,则依次插入所有字段
多个列表和多个值之间使用逗号分隔
值列表和字段名列表一 一对应
如果插入表中的部分字段数据,则字段名列表必填
每个数据值的数据类型、精度和小数位数必须与相应的列匹配
不能为标识列指定值(主键)
如果在设计表的时候就指定了某列不允许为空,则必须插入数据
如果字段具有默认值,可以使用
DEFAULT
关键字来代替插入的数值
INSERT INTO 表名 [(字段名列表)] VALUES (值列表1), (值列表2), (值列表n);
# 增
INSERT INTO gradeinfo (grade) VALUES ('ACCP'),('BCSP');
为避免表结构发生变化引发的错误,建议插入数据时写明具体字段名!
INSERT INTO 表名(字段1,字段2 , ……) SELECT 字段1,字段2 , …… FROM 原表;
# 事先创建且与插入数据字段相符
CREATE TABLE patient_address (SELECT patientName, address FROM patient ) ;
# 无须事先创建
如果新表已存在,将会报错!
UPDATE 表名 SET 字段1=值1, 字段2=值2, …, 字段n=值n [WHERE 条件];
# 更新表数据
UPDATE gradeinfo SET gradeinfo.grade='BCSP' WHERE gradeinfo.id=2;
DELETE FROM 表名 [WHERE条件];
# 删除表信息
DELETE FROM patient WHERE patientID =1
DELETE条件删除语句中,如果未指定WHERE条件语句,则将删除表中的所有数据
DELETE FROM gradeinfo WHERE gradeinfo.id=1;
TRUNCATE TABLE 表名;
# 格式化表
TRUNCATE TABLE gradeinfo;
使用 TRUNCATE 语句删除表中记录后,将重置自增列,表结构及其字段、约束、索引保持不变,执行速度比 DELETE 语句快
DROP | DELETE | TRUNCATE | |
---|---|---|---|
类型 | DDL 会隐式提交,不能回滚 | DML 每次从表中删除一行数据的同时将改行的删除操作记录在redo和undo表空间中,以便进行回滚和重做操作 需要手动提交操作才能生效,可通过ROLLBACK撤销操作 | DDL 会隐式提交,不会记录日志,不能回滚 |
功能 | 删除表结构及所有数据,并将标所占用的空间全部释放 | 可根据条件删除表中满足条件的数据,如果不指定WHERE子句,则删除表中所有记录 | 删除表中所有记录,并将重建表结构 |
一般来说,DROP>TRUNCATE>DELETE
Data Query Language,用于查询数据库的表中数据,是数据库中最为核心的语言,使用频率最高
SELECT * FROM 表名称
SELECT * FROM patient
# 查询 patient 病人表中的所有字段 * 表示所有字段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UdtvUOyE-1680618008628)(./assets/image-20230402180217861.png)]
SELECT 字段1,字段2,字段n FROM 表名称
SELECT patientID,patientName,gender FROM patient
# 查询 patient 病人表中的部分字段(多个字段之间使用,分隔)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3OV0442-1680618008628)(./assets/image-20230402180548009.png)]
有的时候我们不想让用户看到所有的信息,比如用户密码这类的隐私信息,我们就可以通过写具体的字段,就只查询部分信息
在开发中,推荐使用以查询部分列的方式代替
SELECT *
查询全部列,可提高执行效率并养成良好编码习惯
SELECT 字段列表 FROM 表名 AS 表的别名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EvdMoJwV-1680618008629)(./assets/image-20230402185049461.png)]
SELECT 字段1 AS 别名,字段2 FROM 表名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7O4wSRJb-1680618008629)(./assets/image-20230402185113942.png)]
SELECT 字段列表 FROM 表名 表的别名;
# 中间是空格
为某个表命名了别名后,在 SELECT 语句中出现该表的字段需要指定表名时,就必须统一使用该表的别名;否则将产生语法错误
运算符是一种符号,用来进行列间或变量之间的比较和数学运算
MySQL 常的运算符
运算符 | 作用 |
---|---|
+ | 加法 |
- | 减法 |
* | 乘法 |
/ 或 DIV | 除法 |
% 或 MOD | 取余 |
在除法运算和模运算中,如果除数为0,将是非法除数,返回结果为 NULL
符号 | 描述 | 备注 |
---|---|---|
= | 等于 | |
<>, != | 不等于 | |
> | 大于 | |
< | 小于 | |
<= | 小于等于 | |
>= | 大于等于 | |
BETWEEN | 在两值之间 | |
NOT BETWEEN | 不在两值之间 | |
IN | 在集合中 | |
NOT IN | 不在集合中 | |
<=> | 严格比较两个NULL值是否相等 | 两个操作码均为NULL时,其所得值为1;而当一个操作码为NULL时,其所得值为0 |
LIKE | 模糊匹配 | |
REGEXP 或 RLIKE | 正则式匹配 | |
IS NULL | 为空 | |
IS NOT NULL | 不为空 |
运算符号 | 作用 |
---|---|
NOT 或 ! | 逻辑非 |
AND | 逻辑与 |
OR | 逻辑或 |
XOR | 逻辑异或 |
逻辑运算符用来判断表达式的真假。如果表达式是真,结果返回 1。如果表达式是假,结果返回 0
运算符号 | 作用 |
---|---|
& | 按位与 |
| | 按位或 |
^ | 按位异或 |
! | 取反 |
<< | 左移 |
>> | 右移 |
位运算符是在二进制数上进行计算的运算符。位运算会先将操作数变成二进制数,进行位运算。然后再将计算结果从二进制数变回十进制数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SMdejDbc-1680618008630)(./assets/1011652-20170416163043227-1936139924.png)]
如果我们查询时,并不想显示所有的用户信息,只要查出满足指定条件的部分用户,或者精确到某一个用户的信息,就需要使用到 WHERE 关键字对 sql 语句增加查询条件
SELECT * FROM patient WHERE patient.patientID = 1
# 通过患者 ID 精确查询
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UQ6PUGPO-1680618008630)(./assets/image-20230402181442241.png)]
这里我们就把患者 ID 作为查询条件,查询出患者 ID 为 1 的患者信息
SELECT * FROM patient WHERE patient.patientID < 10
# 查询病人 ID 小于 10 的患者信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gRiDtYN2-1680618008630)(./assets/image-20230402182833603.png)]
SELECT * FROM patient WHERE patientName ='玄子'
# 查询所有病人姓名为玄子的患者信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tB2tUCmz-1680618008630)(./assets/image-20230402224936557.png)]
如果查询条件中包含的条件不止一个,则条件根据逻辑关系的不同可以分为与
条件和或
条件两种
与
条件表示要求同时满足两个以上的条件,使用 AND 关键字可以构造与
条件。
或
条件表示几个条件中只需满足其中一个的条件,使用 OR 关键字可以构造或
条件
AND
SELECT * FROM patient WHERE patientName ='玄子' AND address='长春市'
# 多条件查询 姓名为 玄子 且住址为 长春市
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MZDv01Kn-1680618008631)(./assets/image-20230402230857608.png)]
SELECT * FROM patient WHERE patientName ='玄子' OR address='长春市'
# 多条件查询 姓名为 玄子 '或者' 住址为 长春市
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3BI7X4PP-1680618008631)(./assets/image-20230402231059106.png)]
姓名 和 住址 这两个条件满足一个即可
SELECT * FROM patient ORDER BY birthDate
# 通过病人的出生年月进行排序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yaZxJlvk-1680618008631)(./assets/image-20230402183208506.png)]
这里我们使用 ORDER BY 关键字,对病人的出生年月进行排序,默认是从小到大,也就是
升序排列
SELECT * FROM patient ORDER BY birthDate DESC
# 通过病人的出生年月进行降序排序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gkm1A37U-1680618008632)(./assets/image-20230402183508864.png)]
使用降序排列只需要 在最后加上一个
DESC
关键字即可升序排序后面其实也有一个关键字
ASC
不过ORDER BY
默认就是升序排列,所以ASC
加不加都可以
SELECT * FROM patient ORDER BY birthDate ASC,patientID DESC
# 通过病人的出生年月和ID进行多字段排序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-js0iKDAT-1680618008632)(./assets/image-20230402183948955.png)]
如果想要对多个字段进行排序,可直接使用
,
分隔。然后写第二个需要排序的字段和排序方式即可
MySQL 中将一些常用的数据处理操作封装起来,作为函数提供给程序员使用,可以提高程序员开发效率
MySQ L支持的常用函数
字符串函数
时间日期函数
聚合函数
数学函数
函数名 | 作用 | 举例 |
---|---|---|
CONCAT(str1,str2,strn) | 连接字符串str1、str2、strn为一个完整字符串 | SELECT CONCAT( ‘MySQL’,’ is powerful.'); 返回:MySQL is powerful. |
LOWER(str) | 将字符串str中所有字符变为小写 | SELECT LOWER( ‘MySQL is powerful.’); 返回:mysql is powerful. |
UPPER(str) | 将字符串str中所有字符变为大写 | SELECT UPPER( ‘MySQL is powerful.’); 返回:MYSQL IS POWERFUL. |
SUBSTRING(str,num,len) | 返回字符串str的第num个位置开始长度为len的子字符串 | SELECT SUBSTRING( ‘MySQL is powerful.’,10,8); 返回:powerful |
INSERT(str,pos,len,newstr) | 将字符串str从pos位置开始,len个字符长的子串替换为字符串newstr | SELECT INSERT( ‘MySQL is powerful.’,10,0,'very '); 返回:MySQL is very powerful. |
函数名 | 作用 | 举例(部分结果与当前日期有关) |
---|---|---|
CURDATE() | 获取当前日期 | SELECT CURDATE(); 返回:2020-08-03 |
CURTIME() | 获取当前时间 | SELECT CURTIME(); 返回:16:54:40 |
NOW() | 获取当前日期和时间 | SELECT NOW(); 返回:2020-08-03 16:55:00 |
WEEK(date) | 返回日期date为一年中的第几周 | SELECT WEEK(NOW()); 返回:31 |
YEAR(date) | 返回日期date的年份 | SELECT YEAR(NOW()); 返回:2020 |
HOUR(time) | 返回时间time的小时值 | SELECT HOUR(NOW()); 返回:16 |
MINUTE(time) | 返回时间time的分钟值 | SELECT MINUTE(NOW()); 返回:56 |
DATEDIFF(date1,date2) | 返回日期参数date1和date2之间相隔的天数 | SELECT DATEDIFF(NOW(), ‘2019-8-8’); 返回:361 |
ADDDATE(date,n) | 计算日期参数date加上n天后的日期 | SELECT ADDDATE(NOW(), 5); 返回:2020-08-07 16:57:28 |
UNIX_TIMESTAMP(date) | 将日期转换成时间戳 | SELECT UNIX_TIMESTAMP(‘2020-9-1’); 返回:1598889600 |
DATE_ADD(d,INTERVAL expr type) | 计算起始日期 d 加上一个时间段后的日期 | SELECT DATE_ADD(‘2017-06-15’, INTERVAL 10 DAY); 返回:2017-06-25 |
DATE_ADD(d,INTERVAL expr type)
type 参数可以是下列值:MICROSECOND,SECOND,MINUTE,HOUR,DAY,WEEK,MONTH,QUARTER,YEAR,SECOND_MICROSECOND,MINUTE_MICROSECOND,MINUTE_SECOND,HOUR_MICROSECOND,HOUR_SECOND,HOUR_MINUTE,DAY_MICROSECOND,DAY_SECOND,DAY_MINUTE,DAY_HOUR,YEAR_MONTH
函数名 | 作用 |
---|---|
COUNT() | 返回某字段的行数 |
MAX() | 返回某字段的最大值 |
MIN() | 返回某字段的最小值 |
SUM() | 返回某字段的和 |
AVG() | 返回某字段的平均值 |
函数名 | 作用 | 举例 |
---|---|---|
CEIL(x) | 返回大于或等于数值x的最小整数 | SELECT CEIL(-2.1); 返回:32 |
FLOOR(x) | 返回小于或等于数值x的最大整数 | SELECT FLOOR(-2.1); 返回:-3 |
RAND() | 返回0~1间的随机数 | SELECT RAND(); 返回:0.15013303621684485 |
MySQL 中还有许多专业领域或不常用的函数,想具体了解见文章附件:
第八章 8.4 MySQL 函数
如果我们想要对,查询结果分组,比如按照,考试的科目ID 进行分组。就要使用GROUP BY 关键字对 subjectNo
进行分组,首先我们可以看到成绩表中有四个字段分别是studentNo 学生学号
,subjectNo 课程编号
,examDate 考试日期
,studentResult 考试成绩
。其中subjectNo
有三个编号我们就可以对其分组查询查出这三个编号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-62NzzS0C-1680618008632)(./assets/image-20230402190256908.png)]
SELECT subjectNo FROM result GROUP BY subjectNo
# 分组考试成绩表中的科目编号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rmQ5x4I8-1680618008633)(./assets/image-20230402190524327.png)]
这样分组就只能看到成绩表中的科目 ID,并不能查询其他字段,所以我们通常搭配
聚合函数
使用
SELECT subjectNo,AVG(studentResult) FROM result GROUP BY subjectNo
# 分组科目编号,查询对应科目考试成绩的平均分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AUf2api0-1680618008633)(./assets/image-20230402231713206.png)]
我们在使用 GROUP BY 分组后还想要进一步筛选查询结果,就需要使用 HAVING 关键字进一步筛选,这里并不能使用 WHERE
SELECT subjectNo,AVG(studentResult) AS avg FROM result GROUP BY subjectNo HAVING avg >60
# 分组科目编号,查询对应科目考试成绩的平均分 后继续筛选 成绩在 60 分以上的 科目编号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IukpSkZZ-1680618008633)(./assets/image-20230402231957986.png)]
SELECT <字段名列表>
FROM <表名或视图>
[WHERE <查询条件>]
[GROUP BY <分组的字段名>] [HAVING <条件>]
[ORDER BY <排序的字段名> [ASC 或 DESC]]
[LIMIT [位置偏移量,]行数];
SELECT * FROM patient LIMIT 5,5
# 位置偏移量:第1条记录的位置偏移量是0,第2条记录的位置偏移量是1……
# 行数:显示记录的条数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bxmzQDH0-1680618008634)(./assets/image-20230402233340898.png)]
使用LIMIT子句时,第1条记录的位置是0!
LIMIT 子句经常和 ORDER BY 子句一起使用,即先对查询结果进行排序,再根据 LIMIT 子句的参数返回指定的数据
SELECT * FROM patient LIMIT (n-1)*m,m
# 每页 m 个第 n 页
模糊查询一般使用关键字 LIKE 主要用于匹配列中的数据
通配符 | 解释 | 示例 |
---|---|---|
_ | 一个字符 | A LIKE ‘V_’,则符合条件的A如“VR”、“VC”等 |
% | 任意长度的字符串 | B LIKE ‘SQL%’,则符合条件的A如“SQL Server”、“SQL Server高级编程”等 |
[] | 括号中所指定范围内的一个字符 | C LIKE ‘900[1-2]’,则符合条件的C如“9001”或“9002” |
[^] | 不在括号中所指定范围内的任意一个字符 | D LIKE ‘900[^1-2 ]’,则符合条件的D如“9003”或“9007”等 |
select * from Students where StudentName like '张%'
# 名字以张开头的人
select * from Students where StudentName like '%张'
# 名字以张结尾的人
select * from Students where StudentName like '%张%'
# 名字中包含张的人
select * from Students where StudentName like '张_'
# 名字为张姓的单名人
select * from Students where StudentName like '张[1-4]'
# 查询结果为张姓的张1到张4
select * from Students where StudentName like '张[^1-4]'
# 查询结果为张姓的除了张1到张4的所有张姓单名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MnMPri1A-1680618008634)(./assets/image-20230402234207243.png)]
匹配的字符串必须加单引号或者双引号
默认情况下,LIKE 关键字匹配字符串时候不区分大小写,可以在 LIKE 关键字后添加 BINARY 关键字来区分大小写。
如果查询内容中有通配符字符,就需要加转义字符
\
SELECT 字段 FROM 表名 WHERE 列名 [NOT] BETWEEN 起始值 AND 最终值
SELECT * FROM patient WHERE patient.patientID BETWEEN 5 AND 15
# 查询 ID 在 5 到 15 的患者信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-odBvhhxR-1680618008634)(./assets/image-20230402235352933.png)]
NOT 可选参数,表示取反
查询指定范围内所有值,包括起始值和最终值
IS NULL关键字判断该列的值是否为空值,空值不是空字符串
SELECT 字段 FROM 表名 WHERE 列名 IS [NOT] NULL
SELECT * FROM patient WHERE address IS NOT NULL
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QQZaEwyE-1680618008634)(./assets/image-20230402235909456.png)]
NOT 可选参数,表示取反
子查询是一个嵌套在 SELECT、INSERT、UPDATE 或 DELETE 语句或其他子查询中的查询
SELECT …… FROM 表1 WHERE 字段1 比较运算符 (子查询);
SELECT * FROM patient WHERE patientID IN(1,2,3,4,5,9)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ygalQA56-1680618008635)(./assets/image-20230403000309493.png)]
SELECT * FROM patient WHERE patientID IN(SELECT patientID FROM patient WHERE patient.patientID BETWEEN 5 AND 15)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tm1tfpTE-1680618008635)(./assets/image-20230403000418635.png)]
先执行子查询,返回所有来自子查询的结果
再执行外围的父查询,返回查询的最终结果
将子查询和比较运算符联合使用
必须保证子查询返回的值不能多于一个
SELECT …… FROM 表名 WHERE 字段名 IN (子查询);
与in
一样,查询结果有多条使用
获取的值是不包含在查询结果里面的值
SELECT …… FROM 表名 WHERE 字段名 NOT IN (子查询);
DROP TABLE IF EXISTS temp;
CREATE TABLE log (
… … #省略建表语句
) ;
SELECT …… FROM 表名 WHERE EXISTS (子查询);
当一个查询是另一个查询的条件时,称之为子查询
任何允许使用表达式的地方都可以使用子查询
嵌套在父查询SELECT语句的子查询,可包括
SELECT 子句
SELECT (子查询) [AS 列别名] FROM 表名;
FROM 子句
SELECT * FROM (子查询) AS 表别名;
WHERE 子句
GROUP BY 子句
HAVING 子句
通常,将子查询放在比较条件的右边以增加可读性
子查询可以返回单行或多行数据,此时要选择合适的关键字
子查询的返回是单行数据时,比较条件中可以使用比较运算符
子查询的返回是多行数据时,比较条件中需要使用IN或 NOT IN 关键字
如果判断子查询是否有数据返回时,需要使用 EXISTS 或 NOT EXISTS 关键字
只出现在子查询中、而没有出现在父查询中的列不能包含在输出列中
JOIN 按照功能大致分为如下三类
最典型、最常用的连接查询,根据两张表中共同的列进行匹配,两个表存在主外键关系时,通常会使用内连接查询
SELECT …… FROM 表1 INNER JOIN 表2 ON ……
INNER JOIN用来连接两个表
INNER可以省略
ON用来设置两个表之间的关联条件
SELECT * FROM department_checkitem
INNER JOIN department ON department_checkitem.depID=department.depID
INNER JOIN checkitem ON department_checkitem.checkItemID=checkitem.checkItemID
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oo1Hamso-1680618008636)(./assets/image-20230403001247130.png)]
特点
SELECT * FROM prescription
LEFT JOIN patient ON prescription.patientID=patient.patientID
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vwpOSS2t-1680618008636)(./assets/image-20230403001525010.png)]
SELECT * FROM prescription
RIGHT JOIN patient ON prescription.patientID=patient.patientID
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jqxe0fq6-1680618008636)(./assets/image-20230403001641747.png)]
select->distinct->from->join->on->where->group by->having->order by->limit
from->on->join->where->group by(开始使用select中的别名,后面的语句中都可以使用别名)->sum、count、max、avg->having->select->distinct->order by->limit
关于存储过程我只能说请看下图,这是阿里巴巴发布的《阿里巴巴Java开发手册(终极版)v1.3版本》
在 MySQL 第七条中强制指出禁止使用存储过程
所以对于存储过程不必深究,做到会写能看懂即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fuFR4d6Z-1680618008637)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230329084802.png)]
Stored Procedure
优点
缺点
SQL 最大的缺点还是 SQL 语言本身的局限性 SQL 本身是一种结构化查询语言,我们不应该用存储过程处理复杂的业务逻辑让 SQL 回归它
结构化查询语言
的功用。复杂的业务逻辑,还是交给代码去处理吧
CREATE
[DEFINER = { user | CURRENT_USER }]
# 定义DEFINER默认为当前用户
PROCEDURE 存储过程名
[SQL SECURITY { DEFINER | INVOKER } | …]
# 指定DEFINER或INVOKER权限
BEGIN
…
END
特性 | 说明 |
---|---|
LANGUAGE SQL | 表示存储过程语言,默认SQL |
{CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA} | 表示存储过程要做的工作类别默认值为CONTAINS SQL |
SQL SECURITY { DEFINER | INVOKER } | 指定存储过程的执行权限默认值是DEFINERDEFINDER:使用创建者的权限INVOKER:用执行者的权限 |
COMMENT ‘string’ | 存储过程的注释信息 |
如果省略 SQL SECURITY 特性,则使用 DEFINER 属性指定调用者,且调用者必须具有 EXECUTE 权限,必须在 mysql.user 表中如果将 SQL SECURITY 特性指定为 INVOKER,则 DEFINER 属性无效
IN:指输入参数
OUT:指输出参数
INOUT:指输入输出参数
如果需要定义多个参数,需要使用
,
进行分隔
CALL 存储过程名([参数1,参数2, …]);
# 根据存储过程的定义包含相应的参数
存储过程调用类似于Java中的方法调用
SHOW PROCEDURE STATUS
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NFJ5lJ7B-1680618008637)(./assets/image-20230403133422488.png)]
SHOW CREATE PROCEDURE 存储过程名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0tccXxow-1680618008637)(./assets/image-20230403133612810.png)]
ALTER PROCEDURE 存储过程名[特性………]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tC2qX6Is-1680618008638)(./assets/image-20230403133809483.png)]
DROP PROCEDURE 存储过程名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RvTj3iv0-1680618008638)(./assets/image-20230403133956314.png)]
与Java语言类似,定义存储过程时可以使用变量
DECLARE 变量名[,变量名...] 数据类型 [DEFAULT 值];
给变量进行赋值
SET 变量名 = 表达式值[,变量名=表达式...] ;
定义存储过程时,所有局部变量的声明一定要放在存储过程体的开始;否则,会提示语法错误
系统变量
@@
开头,形式为@@变量名
用户自定义变量
@
开头,形式为@变量名
演示案例
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_exam_GetLastExamDateByPatientNameAndDepID`(IN patient_name VARCHAR(50), IN dep_id INT,OUT last_exam_date DATETIME)
BEGIN
#Routine body goes here...
DECLARE patient_id INT; #声明局部变量
SELECT patientID INTO patient_id FROM patient WHERE patientName= patient_name;
SELECT patient_id; #输出病人的ID
SELECT MAX(examDate) INTO last_exam_date FROM prescription WHERE patientID = patient_id AND depID = dep_id;
END
SET @patient_name='夏颖';
SET @dep_id =1;
CALL proc_exam_GetLastExamDateByPatientNameAndDepID(@patient_name, @dep_id, @last);
SELECT @last;
与Java语言的流程控制语句类似,MySQL提供的控制语句
IF 条件 THEN 语句列表
[ELSEIF 条件 THEN 语句列表]
[ELSE 语句列表]
END IF;
根据病人的家庭收入,返还补贴不同比例的医疗费用
- 家庭年收入在5000元以下的返还当年总医疗费用的20%
- 家庭年收入在10000以下的返还当年总医疗费用的15%
- 家庭年收入在30000以下的返还总医疗费用的5%
- 30000元以上或未登记的不享受医疗费用返还
- 输入病人编号和年份,计算该患者当年的应返还的医疗费用
CREATE DEFINER=`root`@`localhost` PROCEDURE `Proc_CH05_4`(IN patient_ID INT ,IN in_year VARCHAR(50),OUT ou_subsidy FLOAT )
BEGIN
DECLARE tital_Cost FLOAT;
DECLARE totial_income FLOAT;
SELECT incomeMoney INTO totial_income FROM income WHERE patientID =patient_ID;
SELECT sum(checkItemCost) INTO tital_Cost FROM prescription
INNER JOIN checkitem ON prescription.checkItemID=checkitem.checkItemID
WHERE patientID=patient_ID AND examDate >= CONCAT(in_year,'-01-01')
AND examDate <= CONCAT(in_year,'-12-31');
IF totial_income>=0 AND totial_income<5000 THEN
SET ou_subsidy =tital_Cost*0.2;
ELSEIF totial_income>=5000 AND totial_income<10000 THEN
SET ou_subsidy =tital_Cost*0.15;
ELSEIF totial_income>=10000 AND totial_income<30000 THEN
SET ou_subsidy =tital_Cost*0.05;
ELSE
SET ou_subsidy =0;
END IF;
END
CASE
WHEN 条件 THEN 语句列表
[WHEN 条件 THEN 语句列表]
[ELSE 语句列表]
END CASE;
CASE 列名
WHEN 条件值 THEN 语句列表
[WHEN 条件值 THEN 语句列表]
[ELSE 语句列表]
END CASE;
使用CASE语句实现返还补贴不同比例的医疗费用
CREATE DEFINER=`root`@`localhost` PROCEDURE `Proc_CH05_5`(IN patient_ID INT ,IN in_year VARCHAR(50),OUT ou_subsidy FLOAT )
BEGIN
DECLARE tital_Cost FLOAT;
DECLARE totial_income FLOAT;
SELECT incomeMoney INTO totial_income FROM income WHERE patientID =patient_ID;
SELECT sum(checkItemCost) INTO tital_Cost FROM prescription
INNER JOIN checkitem ON prescription.checkItemID=checkitem.checkItemID
WHERE patientID=patient_ID AND examDate >= CONCAT(in_year,'-01-01')
AND examDate <= CONCAT(in_year,'-12-31');
CASE
WHEN totial_income>=0 AND totial_income<5000 THEN
SET ou_subsidy =tital_Cost*0.2;
WHEN totial_income>=5000 AND totial_income<10000 THEN
SET ou_subsidy =tital_Cost*0.15;
WHEN totial_income>=10000 AND totial_income<30000 THEN
SET ou_subsidy =tital_Cost*0.05;
WHEN totial_income>=30000 AND totial_income<0 THEN
SET ou_subsidy =0;
END CASE;
END
在某种情况下(例如,做等值判断),使用第二种写法更加简洁但是,因为CASE后面有列名,功能上会有一些限制
[label:] WHILE 条件 DO
语句列表
END WHILE [label]
- 首先判断条件是否成立。如果成立,则执行循环体
- label为标号,用于区分不同的循环,可省略
- 用在begin、repeat、while 或者loop 语句前
假设有测试表test,有Id字段、Val字段
- 根据输入的行数要求,批量插入测试数据
DECLARE rand_val FLOAT;
WHILE rows > 0 DO
SELECT RAND() INTO rand_val;
INSERT INTO test VALUES(NULL, rand_val);
SET rows = rows - 1;
END WHILE;
[label:] LOOP
语句列表
END LOOP [label] ;
不需判断初始条件,直接执行循环体
LEAVE label ;
遇到 LEAVE 语句,退出循环
批量插3个新的检查项目,检查项目名称为胃镜、肠镜和支气管纤维镜,各项检查的价格均为70元
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_checkitem_insert`( IN checkitems VARCHAR(100))
BEGIN
DECLARE comma_pos INT;
DECLARE current_checkitem VARCHAR(20);
loop_label: LOOP
SET comma_pos = LOCATE(',', checkitems);
SET current_checkitem = SUBSTR(checkitems, 1, comma_pos-1);
IF current_checkitem <> '' THEN
SET checkitems = SUBSTR(checkitems, comma_pos+1);
ELSE
SET current_checkitem = checkitems;
END IF;
INSERT INTO checkitem(checkItemName,checkItemCost) VALUES(current_checkitem,70);
IF comma_pos=0 OR current_checkitem='' THEN
LEAVE loop_label;
# 退出loop_label标识的程序块
END IF;
END LOOP loop_label;
# LOOP循环结束
END
[label:] REPEAT
语句列表
UNTIL 条件
END REPEAT [label]
- 先执行循环操作再判断循环条件
- 与 LOOP 循环语句相比较相同点
- 不需要初始条件直接进入循环体
- 不同点:REPEAT 语句可以设置退出条件
使用REPEAT循环语句编码实现,根据输入的行数要求,向测试表test中批量插入测试数据
CREATE DEFINER=`root`@`localhost` PROCEDURE `Proc_CH05_7`(IN rows INT )
BEGIN
DECLARE rand FLOAT;
REPEAT
SELECT RAND() INTO rand;
INSERT INTO test (val)VALUES(rand);
SET rows = rows -1 ;
UNTIL rows <= 0 END REPEAT;
END
ITERATE label;
- 从当前代码处返回到程序块开始位置,重新执行
- ITERATE关键字可以嵌入到LOOP、WHILE和REPEAT程序块中
输入需增加数据行数,随机产生的测试数据必须大于0.5
CREATE DEFINER=`root`@`localhost` PROCEDURE `Proc_CH05_8`(IN rows INT)
BEGIN
DECLARE rand FLOAT;
random_lbl : REPEAT
SELECT RAND() INTO rand;
IF rand< 0.5 THEN
ITERATE random_lbl;
END IF;
INSERT INTO test (val) VALUES (rand);
SET rows=rows-1;
UNTIL rows<=0 END REPEAT;
END
TRANSACTION
事务必须具备的属性,简称 ACID 属性
ACID | 属性描述 |
---|---|
原子性:Atomicity | 事务是一个完整的操作,事务的各步操作是不可分的(原子的),要么都执行,要么都不执行 |
一致性:Consistency | 当事务完成时,数据必须处于一致状态 |
隔离性:Isolation | 并发事务之间彼此隔离、独立,不应以任何方式依赖于或影响其他事务 |
持久性:Durability | 事务完成后,它对数据库的修改被永久保持 |
MySQL中支持事务的存储引擎
InnoDB支持事务操作
通过 UNDO 日志和 REDO 日志实现对事务的支持
UNDO 日志
REDO 日志
MyISAM不支持事务操作
实现事务的方式
在执行命令SET autocommit=0,禁止当前会话的自动提交后,后面的SQL语句将作为事务中的语句一同提交
开始事务
BEGIN ;
或
START TRANSACTION;
提交事务
COMMIT ;
回滚(撤销)事务
ROLLBACK ;
使用事务实现小王和小张之间的转账操作
USE paycorp;
BEGIN;
UPDATE account SET balance=balance-2000 WHERE accountName='小王';
UPDATE account SET balance=balance+2000 WHERE accountName='小张';
COMMIT;
小王和小张的总账户余额和转账前保持一致,数据库中数据从一个一致性状态更新到另一个一致性状态
默认情况下,每条单独的SQL语句视为一个事务
关闭默认提交状态后,可手动开启、关闭事务
关闭/开启自动提交
SET autocommit = 0|1;
关闭自动提交后,从下一条SQL语句开始将会开启新事务,需使用COMMIT或ROLLBACK语句结束该事务
事务尽可能简短
事务中访问的数据量尽量最少
查询数据时尽量不要使用事务
在事务处理过程中尽量不要出现等待用户输入的操作
不同的人员关注不同的数据
保证信息的安全性
视图是一张虚拟表
视图中不存放数据
一个原始表,根据不同用户的不同需求,可以创建不同的视图
开发人员
最终用户
使用SQL语句创建视图
CREATE VIEW view_name
AS
使用SQL语句删除视图
DROP VIEW [IF EXISTS] view_name;
-- 删除前先判断 视图是否存在
使用SQL语句查看视图
SELECT 字段1, 字段2, …… FROM view_name;
视图中可以使用多个表
一个视图可以嵌套另一个视图,但最好不要超过3层
对视图数据进行添加、更新和删除操作会直接影响所引用表中的数据
当视图数据来自多个表时,不允许添加和删除数据
查看所有视图
USE information_schema;
SELECT * FROM views;
使用视图修改数据会有许多限制,一般在实际开发中视图仅用作查询
CREATE VIEW VIEW_1
AS
SELECT prescription.*,patient.patientName FROM prescription INNER JOIN patient ON prescription.patientID=patient.patientID;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tFrhcoZS-1680618008638)(./assets/image-20230403144209697.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kLvask2L-1680618008639)(./assets/image-20230403144259240.png)]
是对数据库表中一列或多列值进行排列的一种结构
作用
大大提高数据库的检索速度
改善数据库性能
MySQL索引按存储类型分类
B-树索引(BTREE)
哈希索引(HASH)
数据存储在数据表中,而索引是创建在数据库表对象上
由表中的一个字段或多个字段生成的键组成
Indexes Use Key Values to Locate Data
(根据索引键查找定位数据行)
普通索引
唯一索引
主键索引
复合索引
全文索引
创建索引
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name ON table_name (column_name [length] …);
-- 唯一索引、全文索引或空间索引,可选
如果创建索引是未指定创建索引的类型,则创建的索引为普通索引通过CREATE INDEX语句无法创建主键索引,主键索引的创建语句
删除索引
ALTER TABLE tablename ADD PRIMARY KEY(column)
删除表时,该表的所有索引同时会被删除
按照下列标准选择建立索引的列
不要使用下面的列创建索引
查询时减少使用*
返回表的全部列,不要返回不需要的列
索引应该尽量小,在字节数小的列上建立索引
WHERE 子句中有多个条件表达式时,包含索引列的表达式应置于其他条件表达式之前
避免在 ORDER BY 子句中使用表达式
根据业务数据发生频率,定期重新生成或重新组织索引,进行碎片整理
查看已创建的索引信息
SHOW INDEX FROM table_name;
删除索引
DROP INDEX index_name ON table_name;
删除表时,该表的所有索引将同时被删
除删除表中的列时,如果要删除的列为索引的组成部分,则该列也会从索引中删除
如果组成索引的所有列都被删除,则整个索引将被删除
可能导致数据丢失的意外状况
数据备份
数据恢复
mysqldump 命令——MySQL的客户端常用逻辑备份工具
mysqldump [options] database [table1,[table2]…] > [path]/filename.sql
mysqldump是DOS系统下的命令
在使用时,无须进入mysql命令行;否则,将无法执行
示例
以root账户登录到MySQL服务器,使用mysqldump命令备份hospital数据库,将SQL脚本保存到e:\hospital.sql中
mysqldump -uroot -proot hospital > e:\hospital.sql
导出的SQL脚本中两种注释
--
开头:关于SQL语句的注释信息/*!
开头, */
结尾:是关于MySQL服务器相关的注释为保证账户密码安全,命令中可不写密码
但是,参数
-p
必须有,回车后根据提示写密码
常用参数选项
参数 | 描述 |
---|---|
-add-drop-table | 在每个CREATE TABLE语句前添加DROP TABLE语句,默认是打开的,可以用-skip-add-drop-table取消 |
–add-locks | 该选项会在INSERT 语句中捆绑一个LOCK TABLE 和UNLOCK TABLE 语句 |
好处:防止记录被再次导入时,其他用户对表进行的操作 | |
-t或-no-create-info | 只导出数据,而不添加CREATE TABLE语句 |
-c或–complete-insert | 在每个INSERT语句的列上加上列名,在数据导入另一个数据库时有用 |
-d或–no-data | 不写表的任何行信息,只转储表的结构 |
备份文件包含的主要信息
Navicat也可以用于导出数据库的备份脚本
在需要恢复数据库数据时,对导出的SQL备份脚本执行导入操作
mysql为DOS命令
mysql –u username –p [dbname] < filename.sql
-- –u 用户名
-- –p 数据库名
-- filename.sql 备份文件名
在执行该语句之前,必须在MySQL服务器中创建新数据库
因为导出的备份文件中只包含表的备份,而不包含创建的库的语句
因此执行导入操作时必须指定数据库名,且该数据库必须存在
除了在命令行中导入数据以外,还可以在数据库已连接状态下导入数据
source filename;
登录MySQL服务后使用
执行该命令前,先创建并选择恢复后的目标数据库
CREATE DATABASE hospitalDB; #创建数据库
USE hospitalDB; #选择要导入数据库的数据库
source e:\hospital.sql #导入数据
# SQL脚本文件后面不要加字符 ;
Navicat中导入数据的操作步骤
运行SQL文件…
快捷菜单项开始
按钮开始导入数据JDBC(Java Database Conectivity)
Java数据库连接技术的简称,提供连接各种常用数据库的能力
JDBC API
内容:供程序员调用的接口与类,集成在java.sql和javax.sql包中,如
DriverManager
JDBC 驱动
JDBC API 主要功能
与数据库建立连接、执行SQL 语句、处理结果
DriverManager :依据数据库的不同,管理JDBC驱动
Connection :负责连接数据库并担任传送数据的任务
Statement :由 Connection 产生、负责执行SQL语句
esultSet:负责保存Statement执行后的查询结果
数据库版本:MySQL5.7
MySQL官网下载对应的JDBC驱动JAR包
驱动类
使用纯Java方式连接数据库
由JDBC驱动直接访问数据库
优点:完全Java代码,快速、跨平台
缺点:访问不同的数据库需要下载专用的JDBC驱动
JDBC驱动由数据库厂商提供
try {
Class.forName(JDBC驱动类);
# 1.加载JDBC驱动
} catch (ClassNotFoundException e) {
//异常输出代码
} //… …
try {
Connection con=DriverManager.getConnection(数据连接字符串,数据库用户名,密码);
// 2.与数据库建立连接
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM table1;");
// 3.发送SQL语句,并得到返回结果
while (rs.next()) {
int x = rs.getInt("a");
String s = rs.getString("b");
float f = rs.getFloat("c");
}
// 4.处理返回结果
rs.close();
stmt.close();
con.close();
// 5.释放资源
} //… …
jdbc:数据库://ip:端口/数据库名称[连接参数=参数值]
连接本地MySQL中hospital数据库
jdbc:mysql://localhost:3306/hospital?serverTimezone=GMT-8
// 我国处于东八区,时区设置为GMT-8
Connection是数据库连接对象的类型
方法 | 作用 |
---|---|
Statement createStatement() | 创建一个Statement对象将SQL语句发送到数据库 |
PreparedStatement prepareStatement(String sql) | 创建一个PreparedStatement对象,将参数化的SQL语句发送到数据库 |
boolean isClosed() | 查询此Connection对象是否已经被关闭。如果已关闭,则返回true;否则返回false |
void close() | 立即释放此Connection对象的数据库和JDBC资源 |
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.*;
public class HospitalConn {
private static Logger logger = LogManager.getLogger(HospitalConn.class.getName());
public static void main(String[] args) {
Connection conn = null;
// 1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hospital?serverTimezone=UTC","root", "root");
System.out.println("数据库连接成功");
} catch (SQLException e) {
logger.error(e);
} finally {
// 3、关闭数据库连接
try {
if (null != conn) {
conn.close();
System.out.println("数据库连接断开");
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
使用JDBC连接数据库时,经常出现的错误
Java执行数据库操作的一个重要接口,在已经建立数据库连接的基础上,向数据库发送要执行的SQL语句
方法 | 作用 |
---|---|
ResultSet executeQuery(String sql) | 可以执行SQL查询并获取ResultSet对象 |
int executeUpdate(String sql) | 可以执行插入、删除、更新的操作,返回值是执行该操作所影响的行数 |
boolean execute(String sql) | 可以执行任意SQL语句。如果结果为 ResultSet 对象,则返回 true;如果其为更新计数或者不存在任何结果,则返回false |
使用 executeQuery() 和 executeUpdate() 方法都需要啊传入 SQL 语句,因此,需要在 Java 中通过字符串拼接获得 SQL 字符串
String类
StringBuffer类
如果使用StringBuffer 生成了 String 类型字符串,可以通过 toString( ) 方法将其转换为一个 String 对象
String patientName="李明";
String gender="男";
String birthDate="2010-09-03";
//使用+拼接字符串
String sql = "insert into patient (patientName,gender,birthDate) values('"+
patientName+"','"+
gender+"','"+
birthDate+"');";
System.out.println(sql);
//使用StringBuffer拼接字符串
StringBuffer sbSql = new StringBuffer("insert into patient (patientName,gender,birthDate)" +
" values('");sbSql.append(patientName+"','");
sbSql.append(gender+"','");
sbSql.append(birthDate+"');");
sql = sbSql.toString();
System.out.println(sql);
- SQL语句中,字符
"
和'
是等效的- 但在Java代码中拼接字符串时使用字符
'
会使代码更加清晰- 也不容易出错引号、逗号或括号等符号必须成对出现
- 可在控制台输出拼接后的字符串,检查SQL语句是否正确
使用Statement接口执行插入数据的操作的方法
如果希望得到插入成功的数据行数,可以使用executeUpdate()方法;否则,使用execute()方法
实现步骤
演示案例
使用JDBC,向hospital数据库病人表中添加一个新的病人记录关键代码
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class HospitalInsert {
private static Logger logger = LogManager.getLogger(HospitalInsert.class.getName());
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
String name = "张菲";
// 姓名
String gender = "女";
// 性别
String birthDate = "1995-02-12";
// 出生日期
String phoneNum = "13887676500";
// 联系电话
String email = "[email protected]";
//邮箱
String password = "909000";
//密码
String identityNum = "610000199502126100";
//身份证号
String address = "北京市";
//地址
// 1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hospital?serverTimezone=UTC", "root", "root");
// 创建Statement对象
stmt = conn.createStatement();
//构造SQL
StringBuffer sbSql = new StringBuffer("insert into patient (patientName,gender,birthDate,phoneNum,email,password,identityNum,address) values ( '");
sbSql.append(name + "','");
sbSql.append(gender + "','");
sbSql.append(birthDate + "','");
sbSql.append(phoneNum + "','");
sbSql.append(email + "','");
sbSql.append(password + "','");
sbSql.append(identityNum + "','");
sbSql.append(address + "');");
System.out.println(sbSql.toString());
//3、执行插入操作
stmt.execute(sbSql.toString());
} catch (SQLException e) {
logger.error(e);
} finally {
// 4、关闭数据库连接
try {
if (null != stmt) {
stmt.close();
}
if (null != conn) {
conn.close();
System.out.println("数据库连接断开");
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
为了避免可能出现的乱码问题,可将指定数据库连接的编码集为UTF8,多个参数间使用字符&进行分隔
jdbc:mysql://localhost:3306/hospital?serverTimezone=GMT-8&useUnicode=true&characterEncoding=utf-8
实现步骤
需关注拼接的SQL字符串,以避免出错
演示案例
使用JDBC,将hospital数据库中patientID为13的病人电话更新为13627395833
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class HospitalUpdate {
private static Logger logger = LogManager.getLogger(HospitalUpdate.class.getName());
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
int patientID = 13;
// 病人编号
String phoneNum = "13627395833";
// 联系电话
// 1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hospital?serverTimezone=UTC", "root", "root");
// 创建Statement对象
stmt = conn.createStatement();
//构造SQL
StringBuffer sbSql = new StringBuffer("update patient ");
sbSql.append("set phoneNum='" + phoneNum + "' ");
sbSql.append("where patientID=" + patientID + ";");
System.out.println(sbSql.toString());
//3、执行插入更新操作
int effectRowNum = stmt.executeUpdate(sbSql.toString());
System.out.println("更新数据的行数:" + effectRowNum);
} catch (SQLException e) {
logger.error(e);
} finally {
// 4、关闭数据库连接
try {
if (null != stmt) {
stmt.close();
}
if (null != conn) {
conn.close();
System.out.println("数据库连接断开");
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
保存和处理Statement执行后所产生的查询结果
方法 | 说明 |
---|---|
boolean next() | 将游标从当前位置向下移动一行 |
void close() | 关闭ResultSet 对象 |
int getInt(int colIndex) | 以int形式获取结果集当前行指定列号值 |
int getInt(String colLabel) | 以int形式获取结果集当前行指定列名值 |
float getFloat(int colIndex) | 以float形式获取结果集当前行指定列号值 |
float getFloat(String colLabel) | 以float形式获取结果集当前行指定列名值 |
String getString(int colIndex) | 以String形式获取结果集当前行指定列号值 |
String getString(String colLabel) | 以String形式获取结果集当前行指定列名值 |
- 要从中获取数据的列号或列名可作为方法的参数
- 根据值的类型选择对应的方法
ResultSet 接口 getXxx() 方法
int类型 -> getInt()
float类型 -> getFloat()
String类型 -> getString()
假设结果集的第一列为patientID,存储类型为int类型,能够获得该列值的两种方法
//使用列号提取数据
int id = rs.getInt(1);
//使用列名提取数据
int id = rs.getInt("patientID");
- 列号从1开始计数,与数组下标从0开始计数不同
- 采用列名来标识列可读性强,且不容易出错
使用 JDBC 从 hospital 数据库中查询前3个病人的编号、姓名、性别、住址信息并输出到控制台上
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.*;
public class HospitalQuery {
private static Logger logger = LogManager.getLogger(HospitalQuery.class.getName());
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
int patientID = 13;
// 病人编号
String phoneNum = "13627395833";
// 联系电话
// 1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hospital?serverTimezone=UTC", "root", "root");
System.out.println("建立连接成功 !");
// 创建Statement对象
stmt = conn.createStatement();
//构造SQL
String sql = "select patientID,patientName,gender,address from patient limit 3;";
//3、执行查询更新操作
rs = stmt.executeQuery(sql);
//4、移动指针遍历结果集并输出查询结果
while (rs.next()) {
System.out.println(rs.getInt("patientID") + "\t" +
rs.getString("patientName") + "\t" +
rs.getString("gender") + "\t" +
rs.getString("address"));
}
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != rs) {
rs.close();
}
if (null != stmt) {
stmt.close();
}
if (null != conn) {
conn.close();
System.out.println("数据库连接断开");
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
通过提交一段SQL代码,执行超出用户访问权限的数据操作称为SQL注入(SQL Injection),SQL注入攻击是应用安全领域的一种常见攻击方式,会造成的数据库安全风险包括:刷库、拖库和撞库等,主要是没有对用户输入数据的合法性进行判断,导致应用程序存在安全隐患
使用JDBC实现医院管理系统用户登录验证功能
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.*;
import java.util.Scanner;
public class HospitalLogin {
private static Logger logger = LogManager.getLogger(HospitalLogin.class.getName());
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
//根据控制台提示输入用户身份证号和密码
Scanner input = new Scanner(System.in);
System.out.println("用户登录");
System.out.print("请输入身份证号:");
String identityNum = input.next();
System.out.print("请输入密码:");
String password = input.next();
// 1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hospital?serverTimezone=UTC", "root", "123456");
// 创建Statement对象
stmt = conn.createStatement();
//构造SQL
StringBuffer sbSql = new StringBuffer("SELECT patientName FROM patient WHERE ");
sbSql.append("password='" + password + "'");
sbSql.append(" and identityNum='" + identityNum + "';");
//3、执行查询更新操作
rs = stmt.executeQuery(sbSql.toString());
System.out.println(sbSql.toString());
//4、验证用户名和密码
if (rs.next()) {
System.out.println("欢迎" + rs.getString("patientName") + "登录系统!");
} else {
System.out.println("密码错误!");
}
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != rs) {
rs.close();
}
if (null != stmt) {
stmt.close();
}
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-80RXlP0a-1680618008639)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230404172438.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OTwEFgfg-1680618008640)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230404172502.png)]
修改查询结构
使用PreparedStatement 接口
PreparedStatement接口 (预编译的 SQL 语句)
提高代码可读性和可维护性
提高安全性
提高SQL语句执行的性能
方 法 | 作 用 |
---|---|
boolean execute() | 执行SQL语句,可以是任何SQL语句。如果结果是Result对象,则返回true。如果结果是更新计数或没有结果,则返回false |
ResultSet executeQuery() | 执行SQL查询,返回该查询生成的ResultSet对象 |
int executeUpdate() | 执行SQL语句,该语句必须是一个DML语句,比如:INSERT、UPDATE或DELETE语句;或者是无返回内容的SQL语句,比如DDL语句。返回值是执行该操作所影响的行数 |
void setXxx(int index,xxx x) | 方法名Xxx和第二个参数的xxx均表示(如int,float,double等)基本数据类型,且两个类型需一致,参数列表中的x表示方法的形式参数。把指定数据类型(xxx)的值x设置给index位置的参数。根据参数类型的不同,常见方法有:setInt(int index,int x) 、setFloat(int index,float x)、setDouble(int index,double x)等 |
void setObject(int index,Object x) | 除基本数据类型外,参数类型也可以是Object,可以将Object对象x设置给index位置的参数 |
创建PreparedStatement对象
设置输入参数的值
执行SQL语句
验证用户输入的身份证号和密码
- 如果通过验证,则输出“欢迎[姓名]登录系统!”的信息;
- 否则输出“密码错误!”
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.*;
import java.util.Scanner;
public class HospitalLogin {
private static Logger logger = LogManager.getLogger(HospitalLogin.class.getName());
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
//根据控制台提示输入用户身份证号和密码
Scanner input = new Scanner(System.in);
System.out.println("用户登录");
System.out.print("请输入身份证号:");
String identityNum = input.next();
System.out.print("请输入密码:");
String password = input.next();
// 1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hospital?serverTimezone=UTC", "root", "123456");
//3、构造PreparedStatement对象
pstmt = conn.prepareStatement("SELECT patientName FROM patient WHERE identityNum=? and password=?");
pstmt.setString(1, identityNum);
pstmt.setString(2, password);
rs = pstmt.executeQuery();
//4、验证用户名和密码
if (rs.next()) {
System.out.println("欢迎" + rs.getString("patientName") + "登录系统!");
} else {
System.out.println("密码错误!");
}
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != rs) {
rs.close();
}
if (null != pstmt) {
pstmt.close();
}
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
实际开发中,推荐使用PreparedStatement接口执行数据库操作
使用JDBC技术访问数据库数据的关键代码
private String driver = "com.mysql.jdbc.Driver";
private String url = "jdbc:mysql://localhost:3306/hospital?serverTimezone=GMT-8";
private String user = “root";
private String password= “123456";
// 修改后需重新编译
Connection conn = null;
public Connection getConnection() {
if(conn==null) {
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {//省略代码……}
}
return conn;// 返回连接对象
}
让用户脱离程序本身修改相关的变量设置——使用配置文件
Java的配置文件常为properties文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hH3NjFkM-1680618008640)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230404204942.png)]
通常,为数据库访问添加的配置文件是database.properties
使用java.util包下的Properties类读取配置文件
方法 | 描述 |
---|---|
String getProperty(String key) | 用指定的键在此属性列表中搜索属性,通过参数key得到其所对应的值 |
Object setProperty(String key, String value) | 通过调用基类Hashtable的put()方法设置键-值对 |
void load(InputStream inStream) | 从输入流中读取属性列表 (键和元素对),通过对指定文件进行装载获取该文件中所有键-值对 |
void clear() | 清除所装载的键-值对,该方法由基类Hashtable提供 |
使用Properties配置文件的方式改造医院管理系统
package XaunZiShare;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;
public class HospitalSystem {
private static Logger logger = LogManager.getLogger(HospitalSystem.class.getName());
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String patientID = null;
boolean isExist = false;
//根据控制台提示输入用户身份证号和密码
Scanner input = new Scanner(System.in);
System.out.println("用户登录");
System.out.print("请输入身份证号:");
String identityNum = input.next();
System.out.print("请输入密码:");
String password = input.next();
Properties params = new Properties();
String configFile = "database.properties";
//配置文件路径
String url = null;
String username = null;
String pwd = null;
//加载配置文件到输入流中
try {
InputStream is = HospitalSystem.class.getClassLoader().getResourceAsStream(configFile);
params.load(is);
//根据指定的获取对应的值
String driver = params.getProperty("driver");
url = params.getProperty("url");
username = params.getProperty("username");
pwd = params.getProperty("password");
// 1、加载驱动
Class.forName(driver);
} catch (IOException e) {
logger.error(e);
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection(url, username, pwd);
//3、构造PreparedStatement对象
pstmt = conn.prepareStatement("SELECT patientID, patientName FROM patient WHERE identityNum=? and password=?");
pstmt.setString(1, identityNum);
pstmt.setString(2, password);
rs = pstmt.executeQuery();
//4、验证用户名和密码
if (rs.next()) {
patientID = rs.getString("patientID");
System.out.println("欢迎" + rs.getString("patientName") + "登录系统!");
while (!isExist) {
System.out.println("1.查询检查记录\t 0.退出");
System.out.print("请输入要执行的操作:");
String action = input.next();
if (action.equals("1")) {
pstmt = conn.prepareStatement("SELECT depName, checkItemName, checkResult, checkItemCost, examDate FROM prescription p INNER JOIN department d ON p.depID = d.depID INNER JOIN checkitem c ON p.checkItemID = c.checkItemID WHERE p.patientID=?;");
pstmt.setString(1, patientID);
rs = pstmt.executeQuery();
System.out.println("检查科室\t检查项目\t检查结果\t检查费用\t检查时间");
while (rs.next()) {
System.out.println(rs.getString("depName") + "\t" + rs.getString("checkItemName") + "\t" + rs.getString("checkResult") + "\t" + rs.getString("checkItemCost") + "\t" + rs.getString("examDate") + "\t");
}
} else if (action.equals("0")) {
isExist = true;
System.out.println("再见");
} else {
System.out.println("输入错误,请重新输入");
}
}
} else {
System.out.println("密码错误!");
}
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != rs) {
rs.close();
}
if (null != pstmt) {
pstmt.close();
}
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
非常流行的数据访问模式——DAO模式
DAO起着转换器的作用,把实体类转换为数据库中的记录
组成部分
优势
数据访问代码和业务逻辑代码之间通过实体类来传输数据
实体类特征
实体类(Entity)是Java应用程序中与数据库表对应的类
package XaunZiShare;
import java.io.Serializable;
public class Patient implements Serializable {
private static final long serialVersionUID = -8762235641468472877L;
private String patientID; //病人编号
private String password; //登录密码
private String birthDate; //出生日期
private String gender; //性别
private String patientName; //姓名
private String phoneNum; //联系电话
private String email; //邮箱
private String identityNum; //身份证号
private String address; //地址
/**
* 无参构造方法
*/
public Patient() {
}
/**
* 有参构造方法,根据需要提供
*
* @param identityNum 身份证号
* @param name 姓名
*/
public Patient(String identityNum, String name) {
this.identityNum = identityNum;
this.patientName = name;
}
public static long getSerialVersionUID() {
return serialVersionUID;
}
public String getPatientID() {
return patientID;
}
public void setPatientID(String patientID) {
this.patientID = patientID;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getBirthDate() {
return birthDate;
}
public void setBirthDate(String birthDate) {
this.birthDate = birthDate;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getPatientName() {
return patientName;
}
public void setPatientName(String patientName) {
this.patientName = patientName;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getIdentityNum() {
return identityNum;
}
public void setIdentityNum(String identityNum) {
this.identityNum = identityNum;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
package XaunZiShare;
import com.javamysql.entity.Patient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;
public class HospitalSystem {
private static Logger logger = LogManager.getLogger(HospitalSystem.class.getName());
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Patient patient = new Patient();
boolean isExist = false;
//根据控制台提示输入用户身份证号和密码
Scanner input = new Scanner(System.in);
System.out.println("用户登录");
System.out.print("请输入身份证号:");
patient.setIdentityNum(input.next());
System.out.print("请输入密码:");
patient.setPassword(input.next());
Properties params = new Properties();
String configFile = "database.properties";
//配置文件路径
String url = null;
String username = null;
String pwd = null;
//加载配置文件到输入流中
try {
InputStream is = HospitalSystem.class.getClassLoader().getResourceAsStream(configFile);
params.load(is);
//根据指定的获取对应的值
String driver = params.getProperty("driver");
url = params.getProperty("url");
username = params.getProperty("username");
pwd = params.getProperty("password");
// 1、加载驱动
Class.forName(driver);
} catch (IOException e) {
logger.error(e);
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection(url, username, pwd);
//3、构造PreparedStatement对象
pstmt = conn.prepareStatement("SELECT * FROM patient WHERE identityNum=? and password=?");
pstmt.setString(1, patient.getIdentityNum());
pstmt.setString(2, patient.getPassword());
rs = pstmt.executeQuery();
//4、验证用户名和密码
if (rs.next()) {
//从MySQL读取用户信息,并加载到patient对象中
patient.setPatientID(rs.getString("patientID"));
patient.setAddress(rs.getString("address"));
patient.setBirthDate(rs.getString("birthDate"));
patient.setEmail(rs.getString("email"));
patient.setGender(rs.getString("gender"));
patient.setPatientID(rs.getString("patientName"));
patient.setPhoneNum(rs.getString("phoneNum"));
System.out.println("欢迎" + patient.getPatientName() + "登录系统!");
while (!isExist) {
System.out.println("1.查询检查记录\t2.查询病人信息\t 0.退出");
System.out.print("请输入要执行的操作:");
String action = input.next();
if (action.equals("1")) {
pstmt = conn.prepareStatement("SELECT depName, checkItemName, checkResult, checkItemCost, examDate FROM prescription p INNER JOIN department d ON p.depID = d.depID INNER JOIN checkitem c ON p.checkItemID = c.checkItemID WHERE p.patientID=?;");
pstmt.setString(1, patient.getPatientID());
rs = pstmt.executeQuery();
System.out.println("检查科室\t检查项目\t检查结果\t检查费用\t检查时间");
while (rs.next()) {
System.out.println(rs.getString("depName") + "\t" + rs.getString("checkItemName") + "\t" + rs.getString("checkResult") + "\t" + rs.getString("checkItemCost") + "\t" + rs.getString("examDate") + "\t");
}
} else if (action.equals("2")) {
System.out.println(patient.getPatientID() + "\t" + patient.getPatientName() + "\t" + patient.getGender() + "\t" + patient.getBirthDate() + "\t" + patient.getIdentityNum() + "\t" + patient.getPhoneNum() + "\t" + patient.getEmail() + "\t" + patient.getAddress());
} else if (action.equals("0")) {
isExist = true;
System.out.println("再见");
} else {
System.out.println("输入错误,请重新输入");
}
}
} else {
System.out.println("密码错误!");
}
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != rs) {
rs.close();
}
if (null != pstmt) {
pstmt.close();
}
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
}
}
实体类特征
如果实体类实现了java.io.Serializable接口,应该定义属性serialVersionUID,解决不同版本之间的序列化问题
为serialVersionUID赋值的方法
手动
使用IDEA生成
private static final long serialVersionUID = -8762235641468472877L;
一旦为一个实体类的serialVersionUID赋值,就不要再修改;否则,在反序列化之前版本的数据时,会报java.io.InvalidClassException异常
将程序中数据在瞬时状态和持久状态间转换的机制为数据持久化
JDBC
数据库
普通文件
XML文件
Scanner input = new Scanner(System.in);
System.out.print("请输入登录名:");
String name=input.next();
System.out.print("请输入登录密码:");
String password=input.next();
// 业务相关代码
// ……省略加载驱动
try {
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/hospital?serverTimezone=GMT-8",
"root", "123456");
// … …省略代码 … …
if(rs.next())
System.out.println("登录成功,欢迎您!");
else
System.out.println("登录失败,请重新输入!");
// ……省略代码
} catch (SQLException e) {
// ……省略代码
} finally {}
// 数据访问代码
业务代码和数据访问代码耦合
采用面向接口编程,可以降低代码间的耦合性
采用面向接口编程,可以降低代码间的耦合性
业务逻辑代码调用数据访问接口
将HospitalSystem中对病人的所有数据库操作抽象成接口
对病人的数据库操作包括修改病人信息、通过身份证号和密码验证登录
设计接口时,尽量以对象为单位,给调用者提供面向对象的接口
接口的命名,应以简明为主
接口由不同数据库的实现类分别实现
PatientDao 接口
package XaunZiShare;
import com.javamysql.entity.Patient;
public interface PatientDao {
/**
* 更新病人信息
*
* @param patient 病人
*/
int update(Patient patient);
/**
* 根据身份证号和登录密码返回病人信息
*
* @param identityNum 身份证号
* @param pwd 登录密码
* @return 病人
*/
Patient getPatientByIdNumAndPwd(String identityNum, String pwd);
}
PatientDao实现类的方法:update()方法
package XaunZiShare;
import com.javamysql.HospitalSystem;
import com.javamysql.dao.PatientDao;
import com.javamysql.entity.Patient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class PatientDaoMySQLImpl implements PatientDao {
private static Logger logger = LogManager.getLogger(HospitalSystem.class.getName());
@Override
public int update(Patient patient) {
Connection conn = null;
PreparedStatement pstmt = null;
int result = 0;
Properties params = new Properties();
String configFile = "database.properties";//配置文件路径
String url = null;
String username = null;
String password = null;
//加载配置文件到输入流中
try {
InputStream is = HospitalSystem.class.getClassLoader().getResourceAsStream(configFile);
params.load(is);
//根据指定的获取对应的值
String driver = params.getProperty("driver");
url = params.getProperty("url");
username = params.getProperty("username");
password = params.getProperty("password");
// 1、加载驱动
Class.forName(driver);
} catch (IOException e) {
logger.error(e);
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection(url, username, password);
//3、构造PreparedStatement对象
pstmt = conn.prepareStatement("UPDATE patient SET address=?, birthDate=?, email=?, gender=?, patientName=?, phoneNum=?, identityNum=?,password=? WHERE patientID=?");
pstmt.setString(1, patient.getAddress());
pstmt.setString(2, patient.getBirthDate());
pstmt.setString(3, patient.getEmail());
pstmt.setString(4, patient.getGender());
pstmt.setString(5, patient.getPatientName());
pstmt.setString(6, patient.getPhoneNum());
pstmt.setString(7, patient.getIdentityNum());
pstmt.setString(8, patient.getPassword());
pstmt.setString(9, patient.getPatientID());
result = pstmt.executeUpdate();
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != pstmt) {
pstmt.close();
}
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
return result;
}
@Override
public Patient getPatientByIdNumAndPwd(String identityNum, String pwd) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Patient patient = null;
Properties params = new Properties();
String configFile = "database.properties";
//配置文件路径
String url = null;
String username = null;
String password = null;
//加载配置文件到输入流中
try {
InputStream is = HospitalSystem.class.getClassLoader().getResourceAsStream(configFile);
params.load(is);
//根据指定的获取对应的值
String driver = params.getProperty("driver");
url = params.getProperty("url");
username = params.getProperty("username");
password = params.getProperty("password");
// 1、加载驱动
Class.forName(driver);
} catch (IOException e) {
logger.error(e);
} catch (ClassNotFoundException e) {
logger.error(e);
}
try {
// 2、建立连接
conn = DriverManager.getConnection(url, username, password);
//3、构造PreparedStatement对象
pstmt = conn.prepareStatement("SELECT * FROM patient WHERE identityNum=? and password=?");
pstmt.setString(1, identityNum);
pstmt.setString(2, pwd);
rs = pstmt.executeQuery();
//4、验证用户名和密码
if (rs.next()) {
//从MySQL读取用户信息,并加载到patient对象中
patient = new Patient();
patient.setPatientID(rs.getString("patientID"));
patient.setAddress(rs.getString("address"));
patient.setBirthDate(rs.getString("birthDate"));
patient.setEmail(rs.getString("email"));
patient.setGender(rs.getString("gender"));
patient.setPatientName(rs.getString("patientName"));
patient.setPhoneNum(rs.getString("phoneNum"));
patient.setIdentityNum(rs.getString("identityNum"));
patient.setPassword(rs.getString("password"));
}
} catch (SQLException e) {
logger.error(e);
} finally {
// 5、关闭数据库连接
try {
if (null != rs) {
rs.close();
}
if (null != pstmt) {
pstmt.close();
}
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
return patient;
}
}
通用的操作是否能够进一步简化?
package XaunZiShare;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class BaseDao {
private static String driver;
// 数据库驱动字符串
private static String url;
// 连接URL字符串
private static String user;
// 数据库用户名
private static String password;
// 用户密码
// 数据连接对象
static {//静态代码块,在类加载的时候执行
init();
}
Connection conn = null;
/**
* 初始化连接参数,从配置文件里获得
*/
public static void init() {
Properties params = new Properties();
String configFile = "database.properties";
//配置文件路径
//加载配置文件到输入流中
InputStream is = BaseDao.class.getClassLoader().getResourceAsStream(configFile);
try {
//从输入流中读取属性列表
params.load(is);
} catch (IOException e) {
e.printStackTrace();
}
//根据指定的获取对应的值
driver = params.getProperty("driver");
url = params.getProperty("url");
user = params.getProperty("username");
password = params.getProperty("password");
}
/**
* 获取数据库连接对象
*/
public Connection getConnection() {
try {
if (conn == null || conn.isClosed()) {
// 获取连接并捕获异常
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
e.printStackTrace();
// 异常处理
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
// 返回连接对象
}
/**
* 关闭数据库连接
*
* @param conn 数据库连接
* @param stmt Statement对象
* @param rs 结果集
*/
public void closeAll(Connection conn, Statement stmt, ResultSet rs) {
// 若结果集对象不为空,则关闭
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 若Statement对象不为空,则关闭
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 若数据库连接对象不为空,则关闭
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 增、删、改的操作
*
* @param preparedSql 预编译的 SQL 语句
* @param param 参数的字符串数组
* @return 影响的行数
*/
public int exceuteUpdate(String preparedSql, Object[] param) {
PreparedStatement pstmt = null;
int num = 0;
conn = getConnection();
try {
pstmt = conn.prepareStatement(preparedSql);
if (param != null) {
for (int i = 0; i < param.length; i++) {
//为预编译sql设置参数
pstmt.setObject(i + 1, param[i]);
}
}
num = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll(conn, pstmt, null);
}
return num;
}
package XaunZiShare;
import com.javamysql.HospitalSystem;
import com.javamysql.dao.BaseDao;
import com.javamysql.dao.PatientDao;
import com.javamysql.entity.Patient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class PatientDaoMySQLImpl extends BaseDao implements PatientDao {
private static Logger logger = LogManager.getLogger(HospitalSystem.class.getName());
@Override
public int update(Patient patient) {
//构造SQL语句
String preparedSQL = "UPDATE patient SET address=?, birthDate=?, email=?, gender=?, patientName=?, phoneNum=?, identityNum=?,password=? WHERE patientID=?";
//构造SQL执行参数数组
List<String> params = new ArrayList<String>();
params.add(patient.getAddress());
params.add(patient.getBirthDate());
params.add(patient.getEmail());
params.add(patient.getGender());
params.add(patient.getPatientName());
params.add(patient.getPhoneNum());
params.add(patient.getIdentityNum());
params.add(patient.getPassword());
params.add(patient.getPatientID());
//调用BaseDao中的更新
return exceuteUpdate(preparedSQL, params.toArray());
}
@Override
public Patient getPatientByIdNumAndPwd(String identityNum, String pwd) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Patient patient = null;
conn = getConnection();
try {
//构造PreparedStatement对象
pstmt = conn.prepareStatement("SELECT * FROM patient WHERE identityNum=? and password=?");
pstmt.setString(1, identityNum);
pstmt.setString(2, pwd);
rs = pstmt.executeQuery();
//验证用户名和密码
if (rs.next()) {
//从MySQL读取用户信息,并加载到patient对象中
patient = new Patient();
patient.setPatientID(rs.getString("patientID"));
patient.setAddress(rs.getString("address"));
patient.setBirthDate(rs.getString("birthDate"));
patient.setEmail(rs.getString("email"));
patient.setGender(rs.getString("gender"));
patient.setPatientName(rs.getString("patientName"));
patient.setPhoneNum(rs.getString("phoneNum"));
patient.setIdentityNum(rs.getString("identityNum"));
patient.setPassword(rs.getString("password"));
}
} catch (SQLException e) {
logger.error(e);
} finally {
//关闭数据库连接
closeAll(conn, pstmt, rs);
}
return patient;
}
}
此种封装JDBC的模式称为DAO模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZbuYr9Zw-1680618008641)(./assets/SRC.png)]
项目名其实就是反过来的网站网址
这个包里面放的是都是,数据表的实体类,(POJO)。即具有,基础属性,属性封装(getter/setter),构造函数,和Tostring()的普通类
dao 包里面还有一层包叫``impl即实现类,dao 包下的接口类只定义方法,具体实现由
impl`包下的实现类实现
service 为业务层,将基础实现类,整合为复杂业务,与dao 包一样包内部还有一层包impl
service 包下的接口类只定义方法,具体实现由impl
包下的实现类实现
代码运行类
项目所需的 JDBC 驱动 放置在此包内
项目连接数据库所需要的配置文件
直接参考阿里巴巴发布的《阿里巴巴Java开发手册(终极版)v1.3版本》
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pU2LMbFe-1680618008641)(./assets/image-20230404213509656.png)]
以下项目都是我练习使用,部分代码可能无法正常运行,当做参考即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AymclqQb-1680618008641)(./assets/SRC-1680615698842-5.png)]
/*
Navicat Premium Data Transfer
Source Server : XuanZi
Source Server Type : MySQL
Source Server Version : 50740 (5.7.40)
Source Host : localhost:3306
Source Schema : block
Target Server Type : MySQL
Target Server Version : 50740 (5.7.40)
File Encoding : 65001
Date: 25/03/2023 01:03:21
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for blocktype
-- ----------------------------
DROP TABLE IF EXISTS `blocktype`;
CREATE TABLE `blocktype` (
`blockId` int(11) NOT NULL AUTO_INCREMENT COMMENT '卡类型id',
`blockType` varchar(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '卡类型',
`discount` double(2, 2) NOT NULL COMMENT '享受折扣',
`minRange` double(8, 2) NOT NULL COMMENT '最小范围',
`maxRange` double(8, 2) NOT NULL COMMENT '最大范围',
PRIMARY KEY (`blockId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of blocktype
-- ----------------------------
INSERT INTO `blocktype` VALUES (1, '白银卡', 0.95, 30.00, 99.00);
INSERT INTO `blocktype` VALUES (2, '黄金卡', 0.90, 100.00, 199.00);
INSERT INTO `blocktype` VALUES (4, '钻石卡', 0.80, 200.00, 499.00);
INSERT INTO `blocktype` VALUES (5, '尊享终生VIP卡', 0.50, 500.00, 1000.00);
-- ----------------------------
-- Table structure for consumption
-- ----------------------------
DROP TABLE IF EXISTS `consumption`;
CREATE TABLE `consumption` (
`consumptionId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`userInfoId` int(11) NOT NULL COMMENT '用户id',
`staffTypeId` int(11) NOT NULL COMMENT '员工类型ID',
`projectTypeId` int(11) NOT NULL COMMENT '项目类型编号',
`consumptionAmount` double(8, 2) NOT NULL COMMENT '消费金额,格局消费的项目和员工类型来决定',
`actualAmount` double(8, 2) NOT NULL COMMENT '实际消费金额,根据用户卡类型打折',
`recordTime` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '消费时间',
PRIMARY KEY (`consumptionId`) USING BTREE,
INDEX `FK_projectTypeId`(`staffTypeId`) USING BTREE,
INDEX `FK_staffId_consumption`(`projectTypeId`) USING BTREE,
INDEX `FK_userInfoId`(`userInfoId`) USING BTREE,
CONSTRAINT `FK_projectTypeId` FOREIGN KEY (`projectTypeId`) REFERENCES `projecttype` (`projectTypeId`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `FK_staffTypeId_1` FOREIGN KEY (`staffTypeId`) REFERENCES `stafftype` (`staffTypeId`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `FK_userInfoId` FOREIGN KEY (`userInfoId`) REFERENCES `userinfo` (`userInfoId`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of consumption
-- ----------------------------
INSERT INTO `consumption` VALUES (1, 1, 3, 4, 30.00, 28.50, '2023-03-24 11:44:06');
INSERT INTO `consumption` VALUES (2, 2, 3, 4, 40.00, 36.00, '2023-03-24 11:57:41');
INSERT INTO `consumption` VALUES (3, 1, 3, 4, 30.00, 28.80, '2023-03-24 16:12:26');
INSERT INTO `consumption` VALUES (5, 1, 1, 1, 50.00, 47.50, '2023-03-24 18:38:47');
-- ----------------------------
-- Table structure for projecttype
-- ----------------------------
DROP TABLE IF EXISTS `projecttype`;
CREATE TABLE `projecttype` (
`projectTypeId` int(11) NOT NULL AUTO_INCREMENT COMMENT '项目类型id',
`projectTypeName` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '项目名称',
`projectTypePrice` double(8, 2) NOT NULL COMMENT '项目价格',
`staffTypeId` int(11) NOT NULL COMMENT '员工类型id',
PRIMARY KEY (`projectTypeId`) USING BTREE,
INDEX `FK_staffTypeId2`(`staffTypeId`) USING BTREE,
CONSTRAINT `FK_staffTypeId2` FOREIGN KEY (`staffTypeId`) REFERENCES `stafftype` (`staffTypeId`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of projecttype
-- ----------------------------
INSERT INTO `projecttype` VALUES (1, '剪头', 50.00, 2);
INSERT INTO `projecttype` VALUES (2, '剪头', 40.00, 3);
INSERT INTO `projecttype` VALUES (3, '剪头', 30.00, 4);
INSERT INTO `projecttype` VALUES (4, '洗剪吹', 80.00, 2);
INSERT INTO `projecttype` VALUES (5, '洗剪吹', 70.00, 3);
INSERT INTO `projecttype` VALUES (6, '洗剪吹', 60.00, 4);
INSERT INTO `projecttype` VALUES (7, '烫头', 110.00, 2);
INSERT INTO `projecttype` VALUES (8, '烫头', 100.00, 3);
INSERT INTO `projecttype` VALUES (9, '烫头', 90.00, 4);
-- ----------------------------
-- Table structure for staff
-- ----------------------------
DROP TABLE IF EXISTS `staff`;
CREATE TABLE `staff` (
`staffId` int(11) NOT NULL AUTO_INCREMENT COMMENT '员工id',
`staffName` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '员工姓名',
`staffTypeId` int(11) NOT NULL COMMENT '员工类型',
PRIMARY KEY (`staffId`) USING BTREE,
INDEX `FK_staffTypeId`(`staffTypeId`) USING BTREE,
CONSTRAINT `FK_staffTypeId` FOREIGN KEY (`staffTypeId`) REFERENCES `stafftype` (`staffTypeId`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of staff
-- ----------------------------
INSERT INTO `staff` VALUES (1, '于昂', 1);
INSERT INTO `staff` VALUES (2, '韩松', 2);
INSERT INTO `staff` VALUES (3, '吕永成', 3);
INSERT INTO `staff` VALUES (4, '敬添', 3);
INSERT INTO `staff` VALUES (5, '方艺科', 4);
INSERT INTO `staff` VALUES (6, '赵梦涛', 4);
INSERT INTO `staff` VALUES (7, '梁瑞康', 4);
-- ----------------------------
-- Table structure for stafftype
-- ----------------------------
DROP TABLE IF EXISTS `stafftype`;
CREATE TABLE `stafftype` (
`staffTypeId` int(11) NOT NULL AUTO_INCREMENT COMMENT '员工类型',
`staffTypeName` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '员工类型名称',
PRIMARY KEY (`staffTypeId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of stafftype
-- ----------------------------
INSERT INTO `stafftype` VALUES (1, '店长');
INSERT INTO `stafftype` VALUES (2, '首席');
INSERT INTO `stafftype` VALUES (3, '资深师傅');
INSERT INTO `stafftype` VALUES (4, '普通员工');
-- ----------------------------
-- Table structure for userinfo
-- ----------------------------
DROP TABLE IF EXISTS `userinfo`;
CREATE TABLE `userinfo` (
`userInfoId` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号 ',
`userInfoName` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户姓名',
`phone` varchar(11) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '电话号码',
`balance` double(8, 2) NOT NULL DEFAULT 0.00 COMMENT '余额',
`staffId` int(11) NOT NULL COMMENT '员工id',
`openBlock` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '开卡时间',
`rechargeable` double(8, 2) NOT NULL COMMENT '第一次充值金额',
`blockId` int(11) NOT NULL COMMENT '卡类型id',
PRIMARY KEY (`userInfoId`) USING BTREE,
INDEX `FK_staffid`(`staffId`) USING BTREE,
INDEX `FK_calorie`(`blockId`) USING BTREE,
CONSTRAINT `FK_blockId` FOREIGN KEY (`blockId`) REFERENCES `blocktype` (`blockId`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `FK_staffid` FOREIGN KEY (`staffId`) REFERENCES `staff` (`staffId`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of userinfo
-- ----------------------------
INSERT INTO `userinfo` VALUES (1, '玄子', '111346518', 20.00, 2, '2023-03-24 23:48:18', 60.00, 1);
INSERT INTO `userinfo` VALUES (2, '玉玉诏', '142141411', 30.00, 2, '2023-03-24 11:21:02', 100.00, 2);
INSERT INTO `userinfo` VALUES (3, '玄子Share', '132131231', 30.00, 2, '2023-03-24 11:33:07', 222.00, 2);
INSERT INTO `userinfo` VALUES (4, '刘景宇', '223213132', 2.00, 2, '2023-03-24 11:33:09', 22.00, 2);
-- ----------------------------
-- Procedure structure for Proc_1
-- ----------------------------
DROP PROCEDURE IF EXISTS `Proc_1`;
delimiter ;;
CREATE PROCEDURE `Proc_1`()
BEGIN
END
;;
delimiter ;
SET FOREIGN_KEY_CHECKS = 1;
driver=com.mysql.jdbc.Driver
url=jdbc\:mysql\://localhost\:3306/block?useUnicode\=true&characterEncoding\=UTF-8
user=root
password=root
BaseDao
package xuanzishare.dao;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class BaseDao {
public static String driver;
// 数据库驱动
public static String url;
// url
public static String user;
// 数据库用户名
public static String password;
// 数据库密码
static {
//静态代码块,在类加载的时候执行
init();
}
public static void init() {
String configFile = "database.properties";
//配置文件路径
//加载配置文件到输入流中
InputStream is = BaseDao.class.getClassLoader().getResourceAsStream(configFile);
Properties params = null;
try {
params = new Properties();
//从输入流中读取属性列表
params.load(is);
} catch (IOException e) {
e.printStackTrace();
}
//根据指定的获取对应的值
driver = params.getProperty("driver");
url = params.getProperty("url");
user = params.getProperty("user");
password = params.getProperty("password");
}
/**
* 得到数据库连接
*
* @return 数据库连接
*/
public static Connection getConn() {
Connection conn = null;
try {
Class.forName(driver);
// 注册驱动
conn = DriverManager.getConnection(url, user, password);
// 获得数据库连接
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
return conn;
// 返回连接
}
public static void closeAll(Connection conn, PreparedStatement ps, ResultSet rs) {
/* 如果rs不空,关闭rs */
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
/* 如果pstmt不空,关闭pstmt */
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
/* 如果conn不空,关闭conn */
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public int updateBuffer(String sql, Object[] objects) {
Connection conn = null;
PreparedStatement ps = null;
int i = 0;
try {
conn = getConn();
ps = conn.prepareStatement(sql);
if (objects != null) {
for (int j = 0; j < objects.length; j++) {
ps.setObject(j + 1, objects[j]);
}
}
System.out.println("\u001B[34mupdateBuffer:" + ps + "\u001B[0m");
i = ps.executeUpdate();
} catch (Exception e) {
throw new RuntimeException(e);
}
closeAll(conn, ps, null);
return i;
}
public double selectAllId(String sql, Object[] objects) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
double allId = 0;
try {
conn = getConn();
ps = conn.prepareStatement(sql);
if (objects != null) {
for (int i = 0; i < objects.length; i++) {
ps.setObject(i + 1, objects[i]);
}
}
System.out.println("\u001B[34mselectAllId:" + ps + "\u001B[0m");
rs = ps.executeQuery();
allId = 0;
while (rs.next()) {
allId = rs.getDouble(1);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
closeAll(conn, ps, rs);
return allId;
}
// 只查询数字类型 把值返还回去
}
BlocktypeDao
package com.block.dao;
import com.block.entity.Blocktype;
import java.util.ArrayList;
public interface BlocktypeDao {
public int updateBlocktypeInfoByBlockId(int blockId, Blocktype blocktype) throws Exception;
// 修改 Blocktype 表字段信息 By BlockId
public ArrayList<Blocktype> selectBlocktypeInfo() throws Exception;
// 查询 Blocktype 表所有字段信息
public int insertBlocktypeInfo(Blocktype blocktype)throws Exception;
// 增加 Blocktype 表所有字段信息
public int deleteBlocktypeInfoByBlockId(int blockId) throws Exception;
// 删除 Blocktype 表所有字段信息 By BlockId
public int slelectBlockIdByUserInfoOfrechargeable(double rechargeable) throws Exception;
// 根据用户缴费数量,决定卡类型ID
}
ConsumptionDao
package com.block.dao;
import com.block.entity.Consumption;
import java.util.ArrayList;
public interface ConsumptionDao {
public ArrayList<Consumption> selectConsumptionInfo() throws Exception;
// 查询 Consumption 表所有字段信息
public int updateConsumptionInfoByConsumptionId(int consumptionId, Consumption consumption) throws Exception;
// 修改 Consumption 表字段信息 By consumptionId
public int insertConsumptionInfo(Consumption consumption) throws Exception;
// 增加 Consumption 表所有字段信息
public int deleteConsumptionInfoByUserInfoId(int userInfoId) throws Exception;
// 删除 Consumption 表所有字段信息 By userInfoId
}
ProjecttypeDao
package com.block.dao;
import com.block.entity.Projecttype;
import java.util.ArrayList;
public interface ProjecttypeDao {
public ArrayList<Projecttype> selectProjecttypeInfo() throws Exception;
// 查询 Projecttype 表所有信息
public double selectProjectTypePriceByProjectTypeNameAndstaffTypeId(String projectTypeName, int staffTypeId) throws Exception;
// 通过项目类型和服务员工类型ID计算价格
public int selectProjectTypeIdByprojectTypeName(String projectTypeName) throws Exception;
// 通过项目名称获取项目ID
}
StaffDao
package com.block.dao;
import com.block.entity.Staff;
import java.util.ArrayList;
public interface StaffDao {
public ArrayList<Staff> selectStaffInfo() throws Exception;
// 查询 Staff 表所有信息
public int selectStafftypeByStaffName(String staffName) throws Exception;
// 通过员工名称查询出员工类型
public int deleteStaffByStaffId(int staffId) throws Exception;
// 删除员工, 先把这个员工 办的卡 算到店长头上,再删除员工信息
public int insertStaffInfo(Staff staff) throws Exception;
// 增加员工信息
}
StafftypeDao
package com.block.dao;
import com.block.entity.Stafftype;
import java.util.ArrayList;
public interface StafftypeDao {
public ArrayList<Stafftype> selectStafftypeInfo() throws Exception;
// 查询 Stafftype 表所有信息
}
UserinfoDao
package com.block.dao;
import com.block.entity.Userinfo;
import java.util.ArrayList;
public interface UserinfoDao {
public ArrayList<Userinfo> selectUserinfoInfo() throws Exception;
// 查询 Userinfo 表所有字段信息
public int insertUserinfoInfo(Userinfo userinfo) throws Exception;
// 增加 Userinfo 表所有字段信息
public int deleteUserinfoInfoByUserInfoId(int userInfoId) throws Exception;
// 删除 Userinfo 表所有字段信息 By userInfoId
public int deleteUserinfoInfoByBlocktypeId(int blocktypeId) throws Exception;
// 删除 Userinfo 通过 BlocktypeId
public int selectUserIdByUserInfoNameAndPhoneLastFour(String userInfoName, String phoneLastFour) throws Exception;
// 通过客户名称和手机号后四位查询用户ID
public double selectDiscountByUserInfoId(int userInfoId) throws Exception;
// 通过用户ID查询卡类型计算折扣
public int updateBalanceByActualAmountAndUserInfoId(double actualAmount, int userInfoId) throws Exception;
// 用户余额减少
public int updateStaffIdByStaffId(int staffId) throws Exception;
// 修改员工 办的卡到店长手下
}
BlocktypeDaoImpl.java
package com.block.dao.impl;
import com.block.dao.BaseDao;
import com.block.dao.BlocktypeDao;
import com.block.entity.Blocktype;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
public class BlocktypeDaoImpl extends BaseDao implements BlocktypeDao {
@Override
public int updateBlocktypeInfoByBlockId(int blockId, Blocktype blocktype) throws Exception {
String sql = "update blocktype set blockType=? ,discount=? ,minRange=? ,maxRange=? where blockId=?;";
Object[] objects = {blocktype.getBlockType(), blocktype.getDiscount(), blocktype.getMinRange(), blocktype.getMaxRange(), blockId};
return updateBuffer(sql, objects);
}
@Override
public ArrayList<Blocktype> selectBlocktypeInfo() throws Exception {
Connection conn = getConn();
ArrayList<Blocktype> blocktypeList = new ArrayList<>();
String sql = "select * from blocktype";
PreparedStatement ps = conn.prepareStatement(sql);
System.err.println("selectBlocktypeInfo:" + ps);
ResultSet rs = ps.executeQuery(sql);
while (rs.next()) {
blocktypeList.add(new Blocktype(
rs.getInt(1),
rs.getString(2),
rs.getDouble(3),
rs.getDouble(4),
rs.getDouble(5)
));
}
closeAll(conn, ps, rs);
return blocktypeList;
}
@Override
public int insertBlocktypeInfo(Blocktype blocktype) throws Exception {
String sql = "insert into blocktype (blockType, discount, minRange, maxRange)\n" +
"values (?,?,?,?)";
Object[] objects = {blocktype.getBlockType(), blocktype.getDiscount(), blocktype.getMinRange(), blocktype.getMaxRange()};
return updateBuffer(sql, objects);
}
@Override
public int deleteBlocktypeInfoByBlockId(int blockId) throws Exception {
String sql = "delete from blocktype where blockId = ?";
Object[] objects = {blockId};
return updateBuffer(sql, objects);
}
@Override
public int slelectBlockIdByUserInfoOfrechargeable(double rechargeable) throws Exception {
Connection conn = getConn();
String sql = "SELECT blockId FROM blocktype WHERE ? Between minRange AND maxRange ";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setDouble(1, rechargeable);
System.err.println("slelectBlockIdByUserInfoOfrechargeable:" + ps);
ResultSet rs = ps.executeQuery();
int blockId = 0;
while (rs.next()) {
blockId = rs.getInt(1);
}
closeAll(conn, ps, rs);
return blockId;
}
}
ConsumptionDaoImpl
package com.block.dao.impl;
import com.block.dao.BaseDao;
import com.block.dao.ConsumptionDao;
import com.block.entity.Consumption;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
public class ConsumptionDaoImpl extends BaseDao implements ConsumptionDao {
@Override
public ArrayList<Consumption> selectConsumptionInfo() throws Exception {
Connection conn = getConn();
ArrayList<Consumption> consumptionList = new ArrayList<>();
String sql = "select * from consumption";
PreparedStatement ps = conn.prepareStatement(sql);
System.err.println("selectConsumptionInfo:" + ps);
ResultSet rs = ps.executeQuery(sql);
while (rs.next()) {
consumptionList.add(new Consumption(
rs.getInt(1),
rs.getInt(2),
rs.getInt(3),
rs.getInt(4),
rs.getDouble(5),
rs.getDouble(6),
rs.getString(7)
));
}
closeAll(conn, ps, rs);
return consumptionList;
}
@Override
public int updateConsumptionInfoByConsumptionId(int consumptionId, Consumption consumption) throws Exception {
return 0;
}
@Override
public int insertConsumptionInfo(Consumption consumption) throws Exception {
String sql = "insert into consumption (userInfoId, staffTypeId, projectTypeId, consumptionAmount, actualAmount)\n" +
"values (?, ?, ?, ?, ?)";
Object[] objects = {consumption.getUserInfoId(), consumption.getProjectTypeId(), consumption.getProjectTypeId(), consumption.getConsumptionAmount(), consumption.getActualAmount()};
return updateBuffer(sql, objects);
}
@Override
public int deleteConsumptionInfoByUserInfoId(int userInfoId) throws Exception {
String sql = "delete from consumption where userInfoId = ?";
Object[] objects = {userInfoId};
return updateBuffer(sql, objects);
}
}
ProjecttypeDaoImpl
package com.block.dao.impl;
import com.block.dao.BaseDao;
import com.block.dao.ProjecttypeDao;
import com.block.entity.Projecttype;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
public class ProjecttypeDaoImpl extends BaseDao implements ProjecttypeDao {
@Override
public ArrayList<Projecttype> selectProjecttypeInfo() throws Exception {
Connection conn = getConn();
ArrayList<Projecttype> projecttypeList = new ArrayList<>();
String sql = "select * from projecttype";
PreparedStatement ps = conn.prepareStatement(sql);
System.err.println("selectProjecttypeInfo:" + ps);
ResultSet rs = ps.executeQuery(sql);
while (rs.next()) {
projecttypeList.add(new Projecttype(
rs.getInt(1),
rs.getString(2),
rs.getDouble(3),
rs.getInt(4)
));
}
closeAll(conn, ps, rs);
return projecttypeList;
}
@Override
public double selectProjectTypePriceByProjectTypeNameAndstaffTypeId(String projectTypeName, int staffTypeId) throws Exception {
Connection conn = getConn();
String sql = "SELECT projectTypePrice FROM projecttype WHERE staffTypeId=? AND projectTypeName=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, staffTypeId);
ps.setString(2, projectTypeName);
System.err.println("selectProjectTypePriceByprojectTypeNameAndstaffTypeId:" + ps);
ResultSet rs = ps.executeQuery();
double projectTypePrice = 0;
while (rs.next()) {
projectTypePrice = rs.getDouble(1);
}
closeAll(conn, ps, rs);
return projectTypePrice;
}
@Override
public int selectProjectTypeIdByprojectTypeName(String projectTypeName) throws Exception {
Connection conn = getConn();
String sql = "SELECT MIN(projectTypeId) FROM projecttype WHERE projectTypeName=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, projectTypeName);
System.err.println("selectProjectTypeIdByprojectTypeName:" + ps);
ResultSet rs = ps.executeQuery();
int projectTypeId = 0;
while (rs.next()) {
projectTypeId = rs.getInt(1);
}
closeAll(conn, ps, rs);
return projectTypeId;
}
}
StaffDaoImpl
package com.block.dao.impl;
import com.block.dao.BaseDao;
import com.block.dao.StaffDao;
import com.block.entity.Staff;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
public class StaffDaoImpl extends BaseDao implements StaffDao {
@Override
public ArrayList<Staff> selectStaffInfo() throws Exception {
Connection conn = getConn();
ArrayList<Staff> staffList = new ArrayList<>();
String sql = "select * from staff";
PreparedStatement ps = conn.prepareStatement(sql);
System.err.println("selectStaffInfo:" + ps);
ResultSet rs = ps.executeQuery(sql);
while (rs.next()) {
staffList.add(new Staff(
rs.getInt(1),
rs.getString(2),
rs.getInt(3)
));
}
closeAll(conn, ps, rs);
return staffList;
}
@Override
public int selectStafftypeByStaffName(String staffName) throws Exception {
Connection conn = getConn();
String sql = "SELECT staffTypeId FROM staff WHERE staffName=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, staffName);
System.err.println("selectStafftypeByStaffName:" + ps);
ResultSet rs = ps.executeQuery();
int Stafftype = 0;
while (rs.next()) {
Stafftype = rs.getInt(1);
}
closeAll(conn, ps, rs);
return Stafftype;
}
@Override
public int deleteStaffByStaffId(int staffId) throws Exception {
String sql = "DELETE FROM staff WHERE staffId=?";
Object[] objects = {staffId};
return updateBuffer(sql, objects);
}
@Override
public int insertStaffInfo(Staff staff) throws Exception {
String sql = "insert into staff (staffName, staffTypeId)\n" +
"values (?,?)";
Object[] objects = {staff.getStaffName(), staff.getStaffTypeId()};
return updateBuffer(sql, objects);
}
}
StafftypeDaoImpl
package com.block.dao.impl;
import com.block.dao.BaseDao;
import com.block.dao.StafftypeDao;
import com.block.entity.Stafftype;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
public class StafftypeDaoImpl extends BaseDao implements StafftypeDao {
@Override
public ArrayList<Stafftype> selectStafftypeInfo() throws Exception {
Connection conn = getConn();
ArrayList<Stafftype> stafftypeList = new ArrayList<>();
String sql = "select * from stafftype";
PreparedStatement ps = conn.prepareStatement(sql);
System.err.println("selectStafftypeInfo:" + ps);
ResultSet rs = ps.executeQuery(sql);
while (rs.next()) {
stafftypeList.add(new Stafftype(
rs.getInt(1),
rs.getString(2)
));
}
closeAll(conn, ps, rs);
return stafftypeList;
}
}
UserinfoDaoImpl
package com.block.dao.impl;
import com.block.dao.BaseDao;
import com.block.dao.UserinfoDao;
import com.block.entity.Userinfo;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
public class UserinfoDaoImpl extends BaseDao implements UserinfoDao {
@Override
public ArrayList<Userinfo> selectUserinfoInfo() throws Exception {
Connection conn = getConn();
ArrayList<Userinfo> userinfoList = new ArrayList<>();
String sql = "select * from userinfo";
PreparedStatement ps = conn.prepareStatement(sql);
System.err.println("selectUserinfoInfo:" + ps);
ResultSet rs = ps.executeQuery(sql);
while (rs.next()) {
userinfoList.add(new Userinfo(
rs.getInt(1),
rs.getString(2),
rs.getString(3),
rs.getDouble(4),
rs.getInt(5),
rs.getString(6),
rs.getDouble(7),
rs.getInt(8)
));
}
closeAll(conn, ps, rs);
return userinfoList;
}
@Override
public int insertUserinfoInfo(Userinfo userinfo) throws Exception {
String sql = "insert into userinfo (userInfoName, phone, balance, staffId, rechargeable, blockId)\n" +
"values (?, ?, ?, ?, ?, ?)";
Object[] objects = {userinfo.getUserInfoName(), userinfo.getPhone(), userinfo.getBalance(), userinfo.getStaffId(), userinfo.getRechargeable(), userinfo.getBlockId()};
return updateBuffer(sql, objects);
}
@Override
public int deleteUserinfoInfoByUserInfoId(int userInfoId) throws Exception {
String sql = "delete from userinfo where userInfoId = ?";
Object[] objects = {userInfoId};
return updateBuffer(sql, objects);
}
@Override
public int deleteUserinfoInfoByBlocktypeId(int blocktypeId) throws Exception {
String sql = "delete from userinfo where blockId = ?";
Object[] objects = {blocktypeId};
return updateBuffer(sql, objects);
}
@Override
public int selectUserIdByUserInfoNameAndPhoneLastFour(String userInfoName, String PhoneLastFour) throws Exception {
Connection conn = getConn();
String sql = "SELECT userInfoId FROM userinfo WHERE userInfoName=? AND phone LIKE '%" + PhoneLastFour + "'";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, userInfoName);
System.err.println("selectUserinfoByUserInfoNameAndPhoneLastFour:" + ps);
ResultSet rs = ps.executeQuery();
int userInfoId = 0;
while (rs.next()) {
userInfoId = rs.getInt(1);
}
closeAll(conn, ps, rs);
return userInfoId;
}
@Override
public double selectDiscountByUserInfoId(int userInfoId) throws Exception {
Connection conn = getConn();
String sql = "SELECT blocktype.discount FROM userinfo INNER JOIN blocktype ON userinfo.blockId=blocktype.blockId WHERE userInfoId =?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, userInfoId);
System.err.println("selectDiscountByUserInfoId:" + ps);
ResultSet rs = ps.executeQuery();
double discount = 0;
while (rs.next()) {
discount = rs.getDouble(1);
}
closeAll(conn, ps, rs);
return discount;
}
@Override
public int updateBalanceByActualAmountAndUserInfoId(double actualAmount, int userInfoId) throws Exception {
String sql = "UPDATE userinfo SET balance=balance-? WHERE userInfoId=?";
Object[] objects = {actualAmount, userInfoId};
return updateBuffer(sql, objects);
}
@Override
public int updateStaffIdByStaffId(int staffId) throws Exception {
String sql = "UPDATE userinfo SET staffId=1 WHERE staffId=?";
Object[] objects = {staffId};
return updateBuffer(sql, objects);
}
}
Blocktype
package com.block.entity;
public class Blocktype {
private int blockId;
private String blockType;
private double discount;
private double minRange;
private double maxRange;
public Blocktype(int blockId, String blockType, double discount, double minRange, double maxRange) {
this.blockId = blockId;
this.blockType = blockType;
this.discount = discount;
this.minRange = minRange;
this.maxRange = maxRange;
}
public Blocktype(String blockType, double discount, double minRange, double maxRange) {
this.blockType = blockType;
this.discount = discount;
this.minRange = minRange;
this.maxRange = maxRange;
}
public Blocktype() {
}
public int getBlockId() {
return blockId;
}
public void setBlockId(int blockId) {
this.blockId = blockId;
}
public String getBlockType() {
return blockType;
}
public void setBlockType(String blockType) {
this.blockType = blockType;
}
public double getDiscount() {
return discount;
}
public void setDiscount(double discount) {
this.discount = discount;
}
public double getMinRange() {
return minRange;
}
public void setMinRange(double minRange) {
this.minRange = minRange;
}
public double getMaxRange() {
return maxRange;
}
public void setMaxRange(double maxRange) {
this.maxRange = maxRange;
}
@Override
public String toString() {
return "Blocktype{" +
"blockId=" + blockId +
", blockType='" + blockType + '\'' +
", discount=" + discount +
", minRange=" + minRange +
", maxRange=" + maxRange +
'}';
}
}
//CREATE TABLE `block`.`blocktype` (
// `blockId` int(11) NOT NULL AUTO_INCREMENT COMMENT '卡类型id',
// `blockType` varchar(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '卡类型',
// `discount` double(2, 2) NOT NULL COMMENT '享受折扣',
// `minRange` double(8, 2) NOT NULL COMMENT '最小范围',
// `maxRange` double(8, 2) NOT NULL COMMENT '最大范围',
// PRIMARY KEY (`blockId`) USING BTREE
//) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;
Consumption
package com.block.entity;
public class Consumption {
private int consumptionId;
private int userInfoId;
private int staffTypeId;
private int projectTypeId;
private double consumptionAmount;
private double actualAmount;
private String recordTime;
public Consumption(int consumptionId, int userInfoId, int staffTypeId, int projectTypeId, double consumptionAmount, double actualAmount, String recordTime) {
this.consumptionId = consumptionId;
this.userInfoId = userInfoId;
this.staffTypeId = staffTypeId;
this.projectTypeId = projectTypeId;
this.consumptionAmount = consumptionAmount;
this.actualAmount = actualAmount;
this.recordTime = recordTime;
}
public Consumption(int userInfoId, int staffTypeId, int projectTypeId, double consumptionAmount, double actualAmount) {
this.userInfoId = userInfoId;
this.staffTypeId = staffTypeId;
this.projectTypeId = projectTypeId;
this.consumptionAmount = consumptionAmount;
this.actualAmount = actualAmount;
}
public Consumption() {
}
public int getConsumptionId() {
return consumptionId;
}
public void setConsumptionId(int consumptionId) {
this.consumptionId = consumptionId;
}
public int getUserInfoId() {
return userInfoId;
}
public void setUserInfoId(int userInfoId) {
this.userInfoId = userInfoId;
}
public int getStaffTypeId() {
return staffTypeId;
}
public void setStaffTypeId(int staffTypeId) {
this.staffTypeId = staffTypeId;
}
public int getProjectTypeId() {
return projectTypeId;
}
public void setProjectTypeId(int projectTypeId) {
this.projectTypeId = projectTypeId;
}
public double getConsumptionAmount() {
return consumptionAmount;
}
public void setConsumptionAmount(double consumptionAmount) {
this.consumptionAmount = consumptionAmount;
}
public double getActualAmount() {
return actualAmount;
}
public void setActualAmount(double actualAmount) {
this.actualAmount = actualAmount;
}
public String getRecordTime() {
return recordTime;
}
public void setRecordTime(String recordTime) {
this.recordTime = recordTime;
}
@Override
public String toString() {
return "Consumption{" +
"consumptionId=" + consumptionId +
", userInfoId=" + userInfoId +
", staffTypeId=" + staffTypeId +
", projectTypeId=" + projectTypeId +
", consumptionAmount=" + consumptionAmount +
", actualAmount=" + actualAmount +
", recordTime='" + recordTime + '\'' +
'}';
}
}
// CREATE TABLE `block`.`consumption` (
// `consumptionId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
// `userInfoId` int(11) NOT NULL COMMENT '用户id',
// `staffTypeId` int(11) NOT NULL COMMENT '员工类型ID',
// `projectTypeId` int(11) NOT NULL COMMENT '项目类型编号',
// `consumptionAmount` double(8, 2) NOT NULL COMMENT '消费金额,格局消费的项目和员工类型来决定',
// `actualAmount` double(8, 2) NOT NULL COMMENT '实际消费金额,根据用户卡类型打折',
// `recordTime` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '消费时间',
// PRIMARY KEY (`consumptionId`) USING BTREE,
// INDEX `FK_projectTypeId`(`staffTypeId`) USING BTREE,
// INDEX `FK_staffId_consumption`(`projectTypeId`) USING BTREE,
// INDEX `FK_userInfoId`(`userInfoId`) USING BTREE,
// CONSTRAINT `FK_projectTypeId` FOREIGN KEY (`projectTypeId`) REFERENCES `block`.`projecttype` (`projectTypeId`) ON DELETE RESTRICT ON UPDATE RESTRICT,
// CONSTRAINT `FK_staffTypeId_1` FOREIGN KEY (`staffTypeId`) REFERENCES `block`.`stafftype` (`staffTypeId`) ON DELETE RESTRICT ON UPDATE RESTRICT,
// CONSTRAINT `FK_userInfoId` FOREIGN KEY (`userInfoId`) REFERENCES `block`.`userinfo` (`userInfoId`) ON DELETE RESTRICT ON UPDATE RESTRICT
//) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;
Projecttype
package com.block.entity;
public class Projecttype {
private int projectTypeId;
private String projectTypeName;
private double projectTypePrice;
private int staffTypeId;
public Projecttype(int projectTypeId, String projectTypeName, double projectTypePrice, int staffTypeId) {
this.projectTypeId = projectTypeId;
this.projectTypeName = projectTypeName;
this.projectTypePrice = projectTypePrice;
this.staffTypeId = staffTypeId;
}
public int getProjectTypeId() {
return projectTypeId;
}
public void setProjectTypeId(int projectTypeId) {
this.projectTypeId = projectTypeId;
}
public String getProjectTypeName() {
return projectTypeName;
}
public void setProjectTypeName(String projectTypeName) {
this.projectTypeName = projectTypeName;
}
public double getProjectTypePrice() {
return projectTypePrice;
}
public void setProjectTypePrice(double projectTypePrice) {
this.projectTypePrice = projectTypePrice;
}
public int getStaffTypeId() {
return staffTypeId;
}
public void setStaffTypeId(int staffTypeId) {
this.staffTypeId = staffTypeId;
}
@Override
public String toString() {
return "Projecttype{" +
"projectTypeId=" + projectTypeId +
", projectTypeName='" + projectTypeName + '\'' +
", projectTypePrice=" + projectTypePrice +
", staffTypeId=" + staffTypeId +
'}';
}
}
// CREATE TABLE `block`.`projecttype` (
// `projectTypeId` int(11) NOT NULL AUTO_INCREMENT COMMENT '项目类型id',
// `projectTypeName` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '项目名称',
// `projectTypePrice` double(8, 2) NOT NULL COMMENT '项目价格',
// `staffTypeId` int(11) NOT NULL COMMENT '员工类型id',
// PRIMARY KEY (`projectTypeId`) USING BTREE,
// INDEX `FK_staffTypeId2`(`staffTypeId`) USING BTREE,
// CONSTRAINT `FK_staffTypeId2` FOREIGN KEY (`staffTypeId`) REFERENCES `block`.`stafftype` (`staffTypeId`) ON DELETE RESTRICT ON UPDATE RESTRICT
//) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;
Staff
package com.block.entity;
public class Staff {
private int staffId;
private String staffName;
private int staffTypeId;
public Staff(int staffId, String staffName, int staffTypeId) {
this.staffId = staffId;
this.staffName = staffName;
this.staffTypeId = staffTypeId;
}
public Staff(String staffName, int staffTypeId) {
this.staffName = staffName;
this.staffTypeId = staffTypeId;
}
public Staff() {
}
public int getStaffId() {
return staffId;
}
public void setStaffId(int staffId) {
this.staffId = staffId;
}
public String getStaffName() {
return staffName;
}
public void setStaffName(String staffName) {
this.staffName = staffName;
}
public int getStaffTypeId() {
return staffTypeId;
}
public void setStaffTypeId(int staffTypeId) {
this.staffTypeId = staffTypeId;
}
@Override
public String toString() {
return "Staff{" +
"staffId=" + staffId +
", staffName='" + staffName + '\'' +
", staffTypeId=" + staffTypeId +
'}';
}
}
// CREATE TABLE `block`.`staff` (
// `staffId` int(11) NOT NULL AUTO_INCREMENT COMMENT '员工id',
// `staffName` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '员工姓名',
// `staffTypeId` int(11) NOT NULL COMMENT '员工类型',
// PRIMARY KEY (`staffId`) USING BTREE,
// INDEX `FK_staffTypeId`(`staffTypeId`) USING BTREE,
// CONSTRAINT `FK_staffTypeId` FOREIGN KEY (`staffTypeId`) REFERENCES `block`.`stafftype` (`staffTypeId`) ON DELETE RESTRICT ON UPDATE RESTRICT
//) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;
Stafftype
package com.block.entity;
public class Stafftype {
private int staffTypeId;
private String staffTypeName;
public Stafftype(int staffTypeId, String staffTypeName) {
this.staffTypeId = staffTypeId;
this.staffTypeName = staffTypeName;
}
public Stafftype() {
}
public int getStaffTypeId() {
return staffTypeId;
}
public void setStaffTypeId(int staffTypeId) {
this.staffTypeId = staffTypeId;
}
public String getStaffTypeName() {
return staffTypeName;
}
public void setStaffTypeName(String staffTypeName) {
this.staffTypeName = staffTypeName;
}
@Override
public String toString() {
return "Stafftype{" +
"staffTypeId=" + staffTypeId +
", staffTypeName='" + staffTypeName + '\'' +
'}';
}
}
// CREATE TABLE `block`.`staff` (
// `staffId` int(11) NOT NULL AUTO_INCREMENT COMMENT '员工id',
// `staffName` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '员工姓名',
// `staffTypeId` int(11) NOT NULL COMMENT '员工类型',
// PRIMARY KEY (`staffId`) USING BTREE,
// INDEX `FK_staffTypeId`(`staffTypeId`) USING BTREE,
// CONSTRAINT `FK_staffTypeId` FOREIGN KEY (`staffTypeId`) REFERENCES `block`.`stafftype` (`staffTypeId`) ON DELETE RESTRICT ON UPDATE RESTRICT
//) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;
Userinfo
package com.block.entity;
public class Userinfo {
private int userInfoId;
private String userInfoName;
private String phone;
private double balance;
private int staffId;
private String openBlock;
private double rechargeable;
private int blockId;
public Userinfo(int userInfoId, String userInfoName, String phone, double balance, int staffId, String openBlock, double rechargeable, int blockId) {
this.userInfoId = userInfoId;
this.userInfoName = userInfoName;
this.phone = phone;
this.balance = balance;
this.staffId = staffId;
this.openBlock = openBlock;
this.rechargeable = rechargeable;
this.blockId = blockId;
}
public Userinfo(String userInfoName, String phone, double balance, int staffId, double rechargeable) {
this.userInfoName = userInfoName;
this.phone = phone;
this.balance = balance;
this.staffId = staffId;
this.rechargeable = rechargeable;
}
public Userinfo(String userInfoName, String phone, double balance, int staffId, double rechargeable, int blockId) {
this.userInfoName = userInfoName;
this.phone = phone;
this.balance = balance;
this.staffId = staffId;
this.rechargeable = rechargeable;
this.blockId = blockId;
}
public int getUserInfoId() {
return userInfoId;
}
public void setUserInfoId(int userInfoId) {
this.userInfoId = userInfoId;
}
public String getUserInfoName() {
return userInfoName;
}
public void setUserInfoName(String userInfoName) {
this.userInfoName = userInfoName;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public int getStaffId() {
return staffId;
}
public void setStaffId(int staffId) {
this.staffId = staffId;
}
public String getOpenBlock() {
return openBlock;
}
public void setOpenBlock(String openBlock) {
this.openBlock = openBlock;
}
public double getRechargeable() {
return rechargeable;
}
public void setRechargeable(double rechargeable) {
this.rechargeable = rechargeable;
}
public int getBlockId() {
return blockId;
}
public void setBlockId(int blockId) {
this.blockId = blockId;
}
@Override
public String toString() {
return "Userinfo{" +
"userInfoId=" + userInfoId +
", userInfoName='" + userInfoName + '\'' +
", phone=" + phone +
", balance=" + balance +
", staffId=" + staffId +
", openBlock='" + openBlock + '\'' +
", rechargeable=" + rechargeable +
", blockId=" + blockId +
'}';
}
}
// CREATE TABLE `block`.`userinfo` (
// `userInfoId` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号 ',
// `userInfoName` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户姓名',
// `phone` int(11) NOT NULL COMMENT '电话号码',
// `balance` double(8, 2) NOT NULL DEFAULT 0.00 COMMENT '余额',
// `staffId` int(11) NOT NULL COMMENT '员工id',
// `openBlock` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '开卡时间',
// `rechargeable` double(8, 2) NOT NULL COMMENT '第一次充值金额',
// `blockId` int(11) NOT NULL COMMENT '卡类型id',
// PRIMARY KEY (`userInfoId`) USING BTREE,
// INDEX `FK_staffid`(`staffId`) USING BTREE,
// INDEX `FK_calorie`(`blockId`) USING BTREE,
// CONSTRAINT `FK_blockId` FOREIGN KEY (`blockId`) REFERENCES `block`.`blocktype` (`blockId`) ON DELETE RESTRICT ON UPDATE RESTRICT,
// CONSTRAINT `FK_staffid` FOREIGN KEY (`staffId`) REFERENCES `block`.`staff` (`staffId`) ON DELETE RESTRICT ON UPDATE RESTRICT
//) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;
//
BlocktypeService
package com.block.service;
public interface BlocktypeService {
public void deleteBlocktypeInfoByBlockId(int blockId) throws Exception;
// 删除 卡 表的外键 然后 删除字段
}
ConsumptionService
package com.block.service;
public interface ConsumptionService {
public void insertConsumptionInfo() throws Exception;
// 增加 Consumption 表所有字段信息
}
StaffService
package com.block.service;
public interface StaffService {
public void deleteStaffByStaffId(int staffId) throws Exception;
// 删除员工信息 先把员工办的卡转移到 店长下 在删除信息
}
UserinfoService
package com.block.service;
import com.block.dao.*;
import com.block.dao.impl.*;
public interface UserinfoService {
public void insertUserinfoInfo() throws Exception;
// 增加 UserinfoDao 表所有字段信息
public void deleteUserinfoInfoByUserInfoId(int userInfoId) throws Exception;
// 删除 UserinfoDao 表所有字段信息 by userInfoId
}
BlocktypeServiceImpl
package com.block.service.impl;
import com.block.dao.*;
import com.block.dao.impl.*;
import com.block.service.BlocktypeService;
import java.util.Scanner;
public class BlocktypeServiceImpl implements BlocktypeService {
Scanner input = new Scanner(System.in);
BlocktypeDao blocktypeDao = new BlocktypeDaoImpl();
ConsumptionDao consumptionDao = new ConsumptionDaoImpl();
ProjecttypeDao projecttypeDao = new ProjecttypeDaoImpl();
StaffDao staffDao = new StaffDaoImpl();
StafftypeDao stafftypeDao = new StafftypeDaoImpl();
UserinfoDao userinfoDao = new UserinfoDaoImpl();
@Override
public void deleteBlocktypeInfoByBlockId(int blockId) throws Exception {
userinfoDao.deleteUserinfoInfoByBlocktypeId(blockId);
int i = blocktypeDao.deleteBlocktypeInfoByBlockId(blockId);
System.out.println("deleteBlocktypeInfoByBlockId:" + i + "行受影响");
}
}
ConsumptionServiceImpl
package com.block.service.impl;
import com.block.dao.*;
import com.block.dao.impl.*;
import com.block.entity.Consumption;
import com.block.service.ConsumptionService;
import java.util.Scanner;
public class ConsumptionServiceImpl implements ConsumptionService {
Scanner input = new Scanner(System.in);
BlocktypeDao blocktypeDao = new BlocktypeDaoImpl();
ConsumptionDao consumptionDao = new ConsumptionDaoImpl();
ProjecttypeDao projecttypeDao = new ProjecttypeDaoImpl();
StaffDao staffDao = new StaffDaoImpl();
StafftypeDao stafftypeDao = new StafftypeDaoImpl();
UserinfoDao userinfoDao = new UserinfoDaoImpl();
@Override
public void insertConsumptionInfo() throws Exception {
System.out.print("请输入用户姓名:");
String userName = input.next();
System.out.print("请输入用户手机号后四位:");
String phoneLastfour = input.next();
// SELECT userInfoId FROM userinfo WHERE userInfoName='玄子' AND phone LIKE '%6518'
int userId = userinfoDao.selectUserIdByUserInfoNameAndPhoneLastFour(userName, phoneLastfour);
System.out.print("请输入消费项目名称:");
String projectTypeName = input.next();
int projectTypeId = projecttypeDao.selectProjectTypeIdByprojectTypeName(projectTypeName);
// SELECT MIN(projectTypeId) FROM projecttype WHERE projectTypeName='剪头'
// 通过项目名称获取项目ID
System.out.print("请输入服务员工名称:");
String staffName = input.next();
int stafftypeId = staffDao.selectStafftypeByStaffName(staffName);
// SELECT staffTypeId FROM staff WHERE staffName='于昂'
// 通过员工名称查询出员工类型
double projectTypePrice = projecttypeDao.selectProjectTypePriceByProjectTypeNameAndstaffTypeId(projectTypeName, stafftypeId);
// SELECT projectTypePrice FROM projecttype WHERE staffTypeId=2 AND projectTypeName='剪头'
// 通过项目类型和服务员工类型计算价格
double discount = userinfoDao.selectDiscountByUserInfoId(userId);
// SELECT blocktype.discount FROM userinfo INNER JOIN blocktype ON userinfo.blockId=blocktype.blockId WHERE userInfoId =1;
// 通过用户ID查询卡类型计算折扣
double actualAmount = projectTypePrice * discount;
userinfoDao.updateBalanceByActualAmountAndUserInfoId(actualAmount, userId);
// 用户余额减少
int i = consumptionDao.insertConsumptionInfo(new Consumption(userId, stafftypeId, projectTypeId, projectTypePrice, actualAmount));
System.out.println("insertConsumptionInfo:" + i + "行受影响");
}
}
StaffServiceImpl
package com.block.service.impl;
import com.block.dao.*;
import com.block.dao.impl.*;
import com.block.service.StaffService;
import java.util.Scanner;
public class StaffServiceImpl implements StaffService {
Scanner input = new Scanner(System.in);
BlocktypeDao blocktypeDao = new BlocktypeDaoImpl();
ConsumptionDao consumptionDao = new ConsumptionDaoImpl();
ProjecttypeDao projecttypeDao = new ProjecttypeDaoImpl();
StaffDao staffDao = new StaffDaoImpl();
StafftypeDao stafftypeDao = new StafftypeDaoImpl();
UserinfoDao userinfoDao = new UserinfoDaoImpl();
@Override
public void deleteStaffByStaffId(int staffId) throws Exception {
userinfoDao.updateStaffIdByStaffId(staffId);
int i = staffDao.deleteStaffByStaffId(staffId);
System.out.println("deleteStaffByStaffId:" + i + "行受影响");
}
}
UserinfoServiceImpl
package com.block.service.impl;
import com.block.dao.*;
import com.block.dao.impl.*;
import com.block.entity.Userinfo;
import com.block.service.UserinfoService;
import java.util.Scanner;
public class UserinfoServiceImpl implements UserinfoService {
Scanner input = new Scanner(System.in);
BlocktypeDao blocktypeDao = new BlocktypeDaoImpl();
ConsumptionDao consumptionDao = new ConsumptionDaoImpl();
ProjecttypeDao projecttypeDao = new ProjecttypeDaoImpl();
StaffDao staffDao = new StaffDaoImpl();
StafftypeDao stafftypeDao = new StafftypeDaoImpl();
UserinfoDao userinfoDao = new UserinfoDaoImpl();
@Override
public void insertUserinfoInfo() throws Exception {
System.out.print("请输入用户名称:");
String userName = input.next();
System.out.print("请输入用户电话号码:");
String userPhone = input.next();
System.out.print("请输入办卡员工ID:");
int staffId = input.nextInt();
System.out.print("请输入用户第一次缴费金额:");
double rechargeabie = input.nextDouble();
int i = userinfoDao.insertUserinfoInfo(new Userinfo(userName, userPhone, rechargeabie, staffId, rechargeabie, blocktypeDao.slelectBlockIdByUserInfoOfrechargeable(rechargeabie)));
System.out.println("insertUserinfoInfo:" + i + "行受影响");
}
@Override
public void deleteUserinfoInfoByUserInfoId(int userInfoId) throws Exception {
consumptionDao.deleteConsumptionInfoByUserInfoId(userInfoId);
int i = userinfoDao.deleteUserinfoInfoByUserInfoId(userInfoId);
System.out.println("insertUserinfoInfo:" + i + "行受影响");
}
}
写到这里 文章是 四万多字 复制代码 Typora 都是卡的。后面的项目就只提供sql文件了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bA5a3zO2-1680618008642)(./assets/image-20230404220024653.png)]
/*
Navicat Premium Data Transfer
Source Server : XuanZi
Source Server Type : MySQL
Source Server Version : 50740 (5.7.40)
Source Host : localhost:3306
Source Schema : myschool
Target Server Type : MySQL
Target Server Version : 50740 (5.7.40)
File Encoding : 65001
Date: 31/03/2023 11:54:12
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for grade
-- ----------------------------
DROP TABLE IF EXISTS `grade`;
CREATE TABLE `grade` (
`gradeID` int(4) NOT NULL AUTO_INCREMENT COMMENT '年级编号',
`gradeName` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '年级名称',
PRIMARY KEY (`gradeID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '年级表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of grade
-- ----------------------------
INSERT INTO `grade` VALUES (1, 'Y1');
INSERT INTO `grade` VALUES (2, 'Y2');
INSERT INTO `grade` VALUES (3, 'Y3');
-- ----------------------------
-- Table structure for result
-- ----------------------------
DROP TABLE IF EXISTS `result`;
CREATE TABLE `result` (
`studentNo` int(4) NOT NULL COMMENT '学号',
`subjectNo` int(4) NOT NULL COMMENT '课程编号',
`examDate` datetime NULL DEFAULT NULL COMMENT '考试日期',
`studentResult` int(4) NOT NULL COMMENT '考试成绩'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '成绩表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of result
-- ----------------------------
INSERT INTO `result` VALUES (10000, 1, '2019-02-17 00:00:00', 71);
INSERT INTO `result` VALUES (10002, 1, '2019-02-17 00:00:00', 83);
INSERT INTO `result` VALUES (10003, 1, '2019-02-17 00:00:00', 60);
INSERT INTO `result` VALUES (10000, 3, '2019-02-17 00:00:00', 60);
INSERT INTO `result` VALUES (10000, 1, '2019-02-17 00:00:00', 71);
INSERT INTO `result` VALUES (10002, 1, '2019-02-17 00:00:00', 83);
INSERT INTO `result` VALUES (10003, 1, '2019-02-17 00:00:00', 60);
INSERT INTO `result` VALUES (10004, 3, '2019-02-17 00:00:00', 60);
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`studentNo` int(4) NOT NULL COMMENT '学号',
`loginPwd` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
`studentName` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '学生姓名',
`sex` char(2) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '男' COMMENT '性别',
`gradeId` int(4) UNSIGNED NULL DEFAULT NULL COMMENT '年级编号',
`phone` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '联系电话',
`address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '地址不详' COMMENT '地址',
`bornDate` datetime NULL DEFAULT NULL COMMENT '出生时间',
`email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮件账号',
`identityCard` varchar(18) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '身份证号码',
PRIMARY KEY (`studentNo`) USING BTREE,
UNIQUE INDEX `identityCard`(`identityCard`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '学生表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (10000, '012345', '郭靖', '男', 1, '13645667783', '天津市河西区', '1990-09-08 00:00:00', NULL, NULL);
INSERT INTO `student` VALUES (10002, '234567', '李斯文', '男', 1, '13645556793', '河南洛阳', '1993-07-23 00:00:00', NULL, NULL);
INSERT INTO `student` VALUES (10003, '345678', '张萍', '女', 1, '13642345112', '地址不详', '1995-06-10 00:00:00', NULL, NULL);
INSERT INTO `student` VALUES (10004, '456789', '韩秋洁', '女', 1, '13812344566', '北京市海淀区', '1995-07-15 00:00:00', NULL, NULL);
INSERT INTO `student` VALUES (10008, '890123', '何睛睛', '女', 1, '13053445221', '广州市天河区', '1997-07-23 00:00:00', NULL, NULL);
-- ----------------------------
-- Table structure for subject
-- ----------------------------
DROP TABLE IF EXISTS `subject`;
CREATE TABLE `subject` (
`subjectNo` int(4) NOT NULL AUTO_INCREMENT COMMENT '课程编号',
`subjectName` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '课程名称',
`classHour` int(4) NULL DEFAULT NULL COMMENT '学时',
`gradeID` int(4) NULL DEFAULT NULL COMMENT '年级编号',
PRIMARY KEY (`subjectNo`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '课程表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of subject
-- ----------------------------
INSERT INTO `subject` VALUES (1, 'Logic Java', 220, 1);
INSERT INTO `subject` VALUES (2, 'HTML', 160, 1);
INSERT INTO `subject` VALUES (3, 'Java OOP', 230, 2);
-- ----------------------------
-- Procedure structure for Proc_select_student
-- ----------------------------
DROP PROCEDURE IF EXISTS `Proc_select_student`;
delimiter ;;
CREATE PROCEDURE `Proc_select_student`(OUT count int)
BEGIN
# 查询所有学生信息
SELECT Count(*) INTO count FROM student;
END
;;
delimiter ;
-- ----------------------------
-- Procedure structure for Proc_studentName
-- ----------------------------
DROP PROCEDURE IF EXISTS `Proc_studentName`;
delimiter ;;
CREATE PROCEDURE `Proc_studentName`(IN student_Name VARCHAR(50) ,OUT last_Result DATETIME)
BEGIN
DECLARE student_No INT;
SELECT studentNo INTO student_No FROM student WHERE studentName=student_Name;
SELECT MAX(examDate) INTO last_Result FROM result WHERE studentNo=student_No ;
END
;;
delimiter ;
SET FOREIGN_KEY_CHECKS = 1;
/*
Navicat Premium Data Transfer
Source Server : XuanZi
Source Server Type : MySQL
Source Server Version : 50740 (5.7.40)
Source Host : localhost:3306
Source Schema : taobao
Target Server Type : MySQL
Target Server Version : 50740 (5.7.40)
File Encoding : 65001
Date: 28/03/2023 11:33:58
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for product
-- ----------------------------
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
`productId` int(4) NOT NULL AUTO_INCREMENT,
`productName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`productPrice` double(10, 2) NULL DEFAULT NULL,
PRIMARY KEY (`productId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of product
-- ----------------------------
INSERT INTO `product` VALUES (1, '农夫山泉', 2.00);
INSERT INTO `product` VALUES (2, '可口可乐', 3.00);
INSERT INTO `product` VALUES (3, '百事可乐', 3.00);
INSERT INTO `product` VALUES (4, '辣条', 2.00);
-- ----------------------------
-- Table structure for shoppingcar
-- ----------------------------
DROP TABLE IF EXISTS `shoppingcar`;
CREATE TABLE `shoppingcar` (
`shoppingCarId` int(4) NOT NULL AUTO_INCREMENT,
`userId` int(4) NOT NULL,
`productId` int(4) NOT NULL,
`productCount` int(4) NOT NULL,
`productTotalCost` double(10, 2) NOT NULL,
`addTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`shoppingCarId`) USING BTREE,
INDEX `userId`(`userId`) USING BTREE,
INDEX `productId`(`productId`) USING BTREE,
CONSTRAINT `shoppingcar_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `userinfo` (`userId`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `shoppingcar_ibfk_2` FOREIGN KEY (`productId`) REFERENCES `product` (`productId`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of shoppingcar
-- ----------------------------
INSERT INTO `shoppingcar` VALUES (1, 1, 1, 1, 20.00, '2023-03-28 10:40:10');
INSERT INTO `shoppingcar` VALUES (2, 1, 2, 2, 30.00, '2023-03-28 10:40:18');
INSERT INTO `shoppingcar` VALUES (3, 2, 3, 2, 22.00, '2023-03-28 10:40:24');
-- ----------------------------
-- Table structure for userinfo
-- ----------------------------
DROP TABLE IF EXISTS `userinfo`;
CREATE TABLE `userinfo` (
`userId` int(4) NOT NULL AUTO_INCREMENT,
`userName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`userPwd` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`userId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of userinfo
-- ----------------------------
INSERT INTO `userinfo` VALUES (1, '玄子', '123456');
INSERT INTO `userinfo` VALUES (2, '玉玉诏', '123456');
SET FOREIGN_KEY_CHECKS = 1;
/*
Navicat Premium Data Transfer
Source Server : XuanZi
Source Server Type : MySQL
Source Server Version : 50740 (5.7.40)
Source Host : localhost:3306
Source Schema : registration
Target Server Type : MySQL
Target Server Version : 50740 (5.7.40)
File Encoding : 65001
Date: 27/03/2023 15:38:03
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for doctor
-- ----------------------------
DROP TABLE IF EXISTS `doctor`;
CREATE TABLE `doctor` (
`doctorId` int(4) NOT NULL AUTO_INCREMENT COMMENT '医生编号',
`doctorName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '医生名称',
`technicalOfficesId` int(4) NOT NULL COMMENT '科室编号',
`doctorHouseId` int(4) NOT NULL COMMENT '诊室编号',
`doctorLevelId` int(4) NOT NULL COMMENT '医生等级ID',
`maxVisits` int(4) NOT NULL COMMENT '最大接诊人数',
PRIMARY KEY (`doctorId`) USING BTREE,
INDEX `PK_01`(`technicalOfficesId`) USING BTREE,
INDEX `PK_02`(`doctorHouseId`) USING BTREE,
INDEX `PK_05`(`doctorLevelId`) USING BTREE,
CONSTRAINT `PK_01` FOREIGN KEY (`technicalOfficesId`) REFERENCES `technicaloffices` (`technicalOfficesId`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `PK_02` FOREIGN KEY (`doctorHouseId`) REFERENCES `doctorhouse` (`doctorHouseId`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `PK_05` FOREIGN KEY (`doctorLevelId`) REFERENCES `doctorlevel` (`doctorLevelId`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of doctor
-- ----------------------------
INSERT INTO `doctor` VALUES (1, '吕永成', 1, 1, 1, 3);
INSERT INTO `doctor` VALUES (2, '韩松', 2, 2, 2, 7);
INSERT INTO `doctor` VALUES (3, '梁瑞康', 3, 3, 3, 9);
-- ----------------------------
-- Table structure for doctorhouse
-- ----------------------------
DROP TABLE IF EXISTS `doctorhouse`;
CREATE TABLE `doctorhouse` (
`doctorHouseId` int(4) NOT NULL AUTO_INCREMENT COMMENT '诊室编号',
`doctorHouseName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '诊室名称',
`technicalOfficesId` int(4) NOT NULL COMMENT '所属科室编号',
PRIMARY KEY (`doctorHouseId`) USING BTREE,
INDEX `PK_03`(`technicalOfficesId`) USING BTREE,
CONSTRAINT `PK_03` FOREIGN KEY (`technicalOfficesId`) REFERENCES `technicaloffices` (`technicalOfficesId`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of doctorhouse
-- ----------------------------
INSERT INTO `doctorhouse` VALUES (1, '血常规', 1);
INSERT INTO `doctorhouse` VALUES (2, '尿常规', 1);
INSERT INTO `doctorhouse` VALUES (3, '血脂、血糖检查', 2);
INSERT INTO `doctorhouse` VALUES (4, '凝血五项', 2);
INSERT INTO `doctorhouse` VALUES (5, '肺炎支、衣原体(快速)', 3);
INSERT INTO `doctorhouse` VALUES (6, 'CT', 3);
INSERT INTO `doctorhouse` VALUES (7, '胃镜', 3);
INSERT INTO `doctorhouse` VALUES (8, '支气管纤维镜', 3);
-- ----------------------------
-- Table structure for doctorlevel
-- ----------------------------
DROP TABLE IF EXISTS `doctorlevel`;
CREATE TABLE `doctorlevel` (
`doctorLevelId` int(4) NOT NULL AUTO_INCREMENT,
`doctorLevelName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`doctorLevelId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of doctorlevel
-- ----------------------------
INSERT INTO `doctorlevel` VALUES (1, '专家');
INSERT INTO `doctorlevel` VALUES (2, '医生');
INSERT INTO `doctorlevel` VALUES (3, '教授');
INSERT INTO `doctorlevel` VALUES (4, '主任');
-- ----------------------------
-- Table structure for patient
-- ----------------------------
DROP TABLE IF EXISTS `patient`;
CREATE TABLE `patient` (
`patientId` int(4) NOT NULL AUTO_INCREMENT COMMENT '患者ID',
`doctorId` int(4) NOT NULL COMMENT '医生ID',
`patientName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '患者名称',
`patientState` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '待' COMMENT '患者状态',
`registrationTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '挂号时间',
`diagnosticTime` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '就诊时间',
PRIMARY KEY (`patientId`) USING BTREE,
INDEX `PK_04`(`doctorId`) USING BTREE,
CONSTRAINT `PK_04` FOREIGN KEY (`doctorId`) REFERENCES `doctor` (`doctorId`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of patient
-- ----------------------------
INSERT INTO `patient` VALUES (1, 1, '玄子', '待', '2023-03-25 10:16:28', '2023-03-25 21:03:49');
INSERT INTO `patient` VALUES (2, 1, '玉玉诏', '待', '2023-03-25 10:16:39', '2023-03-25 21:09:29');
INSERT INTO `patient` VALUES (3, 2, '清玄子', '待', '2023-03-25 10:17:19', '2023-03-25 10:17:19');
INSERT INTO `patient` VALUES (4, 2, '22', '待', '2023-03-25 12:19:00', '2023-03-25 21:16:37');
INSERT INTO `patient` VALUES (8, 1, '方艺科', '待', '2023-03-25 20:50:43', '2023-03-25 21:12:31');
INSERT INTO `patient` VALUES (9, 1, '312', '待', '2023-03-25 20:52:00', '2023-03-25 21:56:53');
INSERT INTO `patient` VALUES (10, 1, '方艺科', '待', '2023-03-25 21:13:14', '2023-03-26 14:26:29');
INSERT INTO `patient` VALUES (11, 2, '312', '待', '2023-03-25 21:14:38', '2023-03-25 21:16:57');
INSERT INTO `patient` VALUES (12, 2, '312', '待', '2023-03-25 21:15:42', '2023-03-25 21:48:10');
INSERT INTO `patient` VALUES (13, 2, '韩松', '待', '2023-03-25 21:15:57', '2023-03-25 21:49:25');
INSERT INTO `patient` VALUES (14, 1, '玄子', '待', '2023-03-26 14:25:59', '2023-03-26 14:26:46');
INSERT INTO `patient` VALUES (15, 2, '玄子', '待', '2023-03-26 14:27:33', '2023-03-26 14:27:45');
-- ----------------------------
-- Table structure for technicaloffices
-- ----------------------------
DROP TABLE IF EXISTS `technicaloffices`;
CREATE TABLE `technicaloffices` (
`technicalOfficesId` int(4) NOT NULL AUTO_INCREMENT COMMENT '科室编号',
`technicalOfficesFloor` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '科室楼层',
`technicalOfficesName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '科室名称',
PRIMARY KEY (`technicalOfficesId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of technicaloffices
-- ----------------------------
INSERT INTO `technicaloffices` VALUES (1, '一楼', '急诊科');
INSERT INTO `technicaloffices` VALUES (2, '二楼', '呼吸科');
INSERT INTO `technicaloffices` VALUES (3, '三楼', '内科');
SET FOREIGN_KEY_CHECKS = 1;
/*
Navicat Premium Data Transfer
Source Server : XuanZi
Source Server Type : MySQL
Source Server Version : 50740 (5.7.40)
Source Host : localhost:3306
Source Schema : wantedjob
Target Server Type : MySQL
Target Server Version : 50740 (5.7.40)
File Encoding : 65001
Date: 30/03/2023 19:05:27
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for collect
-- ----------------------------
DROP TABLE IF EXISTS `collect`;
CREATE TABLE `collect` (
`collectId` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`initiativeCollect` int(11) NOT NULL COMMENT '收藏者',
`passiveCollect` int(11) NOT NULL COMMENT '被收藏者',
`CollectType` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '收藏者关系 u-c c-u',
PRIMARY KEY (`collectId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of collect
-- ----------------------------
INSERT INTO `collect` VALUES (1, 1, 1, 'u-c');
INSERT INTO `collect` VALUES (2, 1, 2, 'u-c');
INSERT INTO `collect` VALUES (3, 2, 2, 'c-u');
INSERT INTO `collect` VALUES (4, 1, 2, 'u-c');
INSERT INTO `collect` VALUES (5, 2, 1, 'u-c');
INSERT INTO `collect` VALUES (6, 3, 1, 'u-c');
INSERT INTO `collect` VALUES (7, 1, 2, 'c-u');
INSERT INTO `collect` VALUES (8, 1, 2, 'c-u');
INSERT INTO `collect` VALUES (9, 1, 1, 'u-c');
-- ----------------------------
-- Table structure for company
-- ----------------------------
DROP TABLE IF EXISTS `company`;
CREATE TABLE `company` (
`companyId` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`companyName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '公司名称',
`companyTypeId` int(11) NOT NULL COMMENT '公司类型编号',
`companyMinEmployeeNum` int(11) NULL DEFAULT NULL COMMENT '公司规模最小人数',
`companyMaxEmployeeNum` int(11) NULL DEFAULT NULL COMMENT '公司规模最大人数',
PRIMARY KEY (`companyId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of company
-- ----------------------------
INSERT INTO `company` VALUES (1, '玄子责任有限公司', 1, 20, 99);
-- ----------------------------
-- Table structure for companytype
-- ----------------------------
DROP TABLE IF EXISTS `companytype`;
CREATE TABLE `companytype` (
`companyTypeId` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`companyTypeName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '公司类型名称',
PRIMARY KEY (`companyTypeId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of companytype
-- ----------------------------
INSERT INTO `companytype` VALUES (1, '软件开发公司');
INSERT INTO `companytype` VALUES (2, '平面设计公司');
-- ----------------------------
-- Table structure for deliver
-- ----------------------------
DROP TABLE IF EXISTS `deliver`;
CREATE TABLE `deliver` (
`deliverId` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`companyId` int(11) NOT NULL COMMENT '公司Id',
`userInfoId` int(11) NOT NULL COMMENT '用户Id',
PRIMARY KEY (`deliverId`) USING BTREE,
INDEX `companyId`(`companyId`) USING BTREE,
INDEX `userInfoId`(`userInfoId`) USING BTREE,
CONSTRAINT `deliver_ibfk_1` FOREIGN KEY (`companyId`) REFERENCES `company` (`companyId`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `deliver_ibfk_2` FOREIGN KEY (`userInfoId`) REFERENCES `userinfo` (`userInfoId`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of deliver
-- ----------------------------
INSERT INTO `deliver` VALUES (1, 1, 1);
INSERT INTO `deliver` VALUES (2, 1, 2);
INSERT INTO `deliver` VALUES (3, 1, 3);
INSERT INTO `deliver` VALUES (4, 1, 1);
-- ----------------------------
-- Table structure for education
-- ----------------------------
DROP TABLE IF EXISTS `education`;
CREATE TABLE `education` (
`educationId` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`educationName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '学历名称',
PRIMARY KEY (`educationId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of education
-- ----------------------------
INSERT INTO `education` VALUES (1, '中专');
INSERT INTO `education` VALUES (2, '大专');
INSERT INTO `education` VALUES (3, '本科');
INSERT INTO `education` VALUES (4, '硕士');
INSERT INTO `education` VALUES (5, '研究生');
-- ----------------------------
-- Table structure for job
-- ----------------------------
DROP TABLE IF EXISTS `job`;
CREATE TABLE `job` (
`jobId` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`jobName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '职业名称',
`jobTypeId` int(11) NULL DEFAULT NULL COMMENT '职业类型编号',
PRIMARY KEY (`jobId`) USING BTREE,
INDEX `jobTypeId`(`jobTypeId`) USING BTREE,
CONSTRAINT `job_ibfk_1` FOREIGN KEY (`jobTypeId`) REFERENCES `jobtype` (`jobTypeId`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of job
-- ----------------------------
INSERT INTO `job` VALUES (1, 'java开发', 1);
INSERT INTO `job` VALUES (2, '前端开发', 1);
INSERT INTO `job` VALUES (3, 'android开发', 1);
INSERT INTO `job` VALUES (4, 'PS美工', 2);
-- ----------------------------
-- Table structure for jobinfo
-- ----------------------------
DROP TABLE IF EXISTS `jobinfo`;
CREATE TABLE `jobinfo` (
`jobInfoId` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`userInfoId` int(11) NULL DEFAULT NULL COMMENT '用户Id',
`jobId` int(11) NULL DEFAULT NULL COMMENT '职业名称Id',
`jobInfoTitle` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '职业信息标题',
`jobInfoMinSalary` double(10, 2) NULL DEFAULT NULL COMMENT '最低薪资(xxK)',
`jobInfoMaxSalary` double(10, 2) NULL DEFAULT NULL COMMENT '最高薪资(xxK)',
`experienceMinYear` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '工作经验最低年限',
`experienceMaxYear` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '工作经验最高年限',
`educationMinId` int(11) NULL DEFAULT NULL COMMENT '最低学历编号',
`region` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地区',
PRIMARY KEY (`jobInfoId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of jobinfo
-- ----------------------------
INSERT INTO `jobinfo` VALUES (1, 2, 1, 'java开发【五险一金】', 1.00, 1000.00, '1', '3', 2, '河南省-郑州市-金水区');
INSERT INTO `jobinfo` VALUES (2, 2, 1, 'java开发【五险一金】', 300.00, 5000.00, '1', '3', 3, '金水区');
INSERT INTO `jobinfo` VALUES (3, 3, 3, '前端开发', 100.00, 10000.00, '1', '3', 2, '中原区');
-- ----------------------------
-- Table structure for jobtype
-- ----------------------------
DROP TABLE IF EXISTS `jobtype`;
CREATE TABLE `jobtype` (
`jobTypeId` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`jobTypeName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '职业类型名',
PRIMARY KEY (`jobTypeId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of jobtype
-- ----------------------------
INSERT INTO `jobtype` VALUES (1, '软件开发');
INSERT INTO `jobtype` VALUES (2, '平面设计');
INSERT INTO `jobtype` VALUES (3, '影视后期');
INSERT INTO `jobtype` VALUES (4, '室内设计');
-- ----------------------------
-- Table structure for resume
-- ----------------------------
DROP TABLE IF EXISTS `resume`;
CREATE TABLE `resume` (
`resumeId` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`userInfoId` int(11) NULL DEFAULT NULL COMMENT '用户Id',
`jobId` int(11) NULL DEFAULT NULL COMMENT '职业名称Id',
`jobInfoMinSalary` double(10, 2) NULL DEFAULT NULL COMMENT '最低薪资(xxK)',
`jobInfoMaxSalary` double(10, 2) NOT NULL COMMENT '最高薪资(xxK)',
`experienceMinYear` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '工作经验最低年限',
`experienceMaxYear` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '工作经验最高年限',
`educationMaxId` int(11) NULL DEFAULT NULL COMMENT '最低学历编号',
`region` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地区',
PRIMARY KEY (`resumeId`) USING BTREE,
INDEX `userInfoId`(`userInfoId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of resume
-- ----------------------------
INSERT INTO `resume` VALUES (1, 1, 1, 1.00, 1.00, '1', '1', 3, '中原区');
INSERT INTO `resume` VALUES (2, 1, 1, 1.00, 1.00, '1', '1', 3, '中原区');
INSERT INTO `resume` VALUES (3, 2, 1, 300.00, 500.00, '1', '3', 3, '金水区');
INSERT INTO `resume` VALUES (4, 3, 1, 300.00, 500.00, '1', '3', 3, '金水区2');
INSERT INTO `resume` VALUES (5, 3, 1, 300.00, 500.00, '1', '3', 3, '金水区2');
-- ----------------------------
-- Table structure for userinfo
-- ----------------------------
DROP TABLE IF EXISTS `userinfo`;
CREATE TABLE `userinfo` (
`userInfoId` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`userInfoName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
`userInfoAccount` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '账号',
`userInfoPwd` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
`userInfoPhone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '联系电话',
`userInfoTypeId` int(11) NOT NULL COMMENT '用户类型Id',
`companyId` int(11) NULL DEFAULT NULL COMMENT '公司Id',
PRIMARY KEY (`userInfoId`) USING BTREE,
INDEX `userInfoTypeId`(`userInfoTypeId`) USING BTREE,
CONSTRAINT `userinfo_ibfk_1` FOREIGN KEY (`userInfoTypeId`) REFERENCES `usertype` (`userTypeId`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of userinfo
-- ----------------------------
INSERT INTO `userinfo` VALUES (1, '玄子', '12345', '12345', '12314211', 1, -1);
INSERT INTO `userinfo` VALUES (2, '玉玉诏', '54321', '12345', '13534151', 2, 1);
INSERT INTO `userinfo` VALUES (3, '123', '3213', '321', '32131', 3, 1);
INSERT INTO `userinfo` VALUES (5, '123', '123456', '12345', '32131', 3, 1);
INSERT INTO `userinfo` VALUES (6, '玄子', '3214', '2142', '1412421', 1, -1);
INSERT INTO `userinfo` VALUES (7, 'eqw', 'ewq', 'eqw', 'eeqw', 2, 1);
-- ----------------------------
-- Table structure for usertype
-- ----------------------------
DROP TABLE IF EXISTS `usertype`;
CREATE TABLE `usertype` (
`userTypeId` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`userTypeName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户类型',
PRIMARY KEY (`userTypeId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of usertype
-- ----------------------------
INSERT INTO `usertype` VALUES (1, '求职者');
INSERT INTO `usertype` VALUES (2, '招聘公司');
INSERT INTO `usertype` VALUES (3, '猎头');
SET FOREIGN_KEY_CHECKS = 1;
WantedJob 求职项目 | |||||||||
---|---|---|---|---|---|---|---|---|---|
用户信息 | userInfo | ||||||||
userInfoId | userInfoName | userInfoAccount | userInfoPwd | userInfoPhone | userInfoTypeId | companyId | |||
用户名 | 账号 | 密码 | 联系电话 | 用户类型Id | 公司Id | ||||
用户类型 | userType | ||||||||
userTypeId | userTypeName | ||||||||
用户类型 | 求职者、招聘公司、猎头 | ||||||||
职业类型 | jobType | ||||||||
jobTypeId | jobTypeName | ||||||||
职业类型名 | |||||||||
职业名称 | job | ||||||||
jobId | jobName | jobTypeId | |||||||
职业名称 | 职业类型编号 | ||||||||
公司类型 | companyType | ||||||||
companyTypeId | companyTypeName | ||||||||
公司类型名称 | |||||||||
公司信息 | company | ||||||||
companyId | companyName | companyTypeId | companyMinEmployeeNum | companyMaxEmployeeNum | |||||
公司名称 | 公司类型编号 | 公司规模最小人数 | 公司规模最大人数 | ||||||
职业信息(发布者) | jobInfo | ||||||||
jobInfoId | userInfoId | jobId | jobInfoTitle | jobInfoMinSalary | jobInfoMaxSalary | experienceMinYear | experienceMaxYear | educationMinId | region |
用户Id | 职业名称Id | 职业信息标题 | 最低薪资(xxK) | 最高薪资(xxK) | 工作经验最低年限 | 工作经验最高年限 | 最低学历编号 | 地区 | |
简历信息(求职者) | resume | ||||||||
resumeId | userInfoId | jobId | jobInfoMinSalary | jobInfoMaxSalary | experienceMinYear | experienceMaxYear | educationMinId | region | |
用户Id | 职业名称Id | 最低薪资(xxK) | 最高薪资(xxK) | 工作经验最低年限 | 工作经验最高年限 | 最低学历编号 | 地区 | ||
投递简历(用户投递公司) | deliver | ||||||||
deliverId | companyId | userInfoId | |||||||
公司Id | 用户Id | ||||||||
收藏 | collect | ||||||||
collectId | initiativeCollect | passiveCollect | |||||||
收藏者 | 被收藏者 | ||||||||
学历 | education | ||||||||
educationId | educationName | ||||||||
学历名称 | |||||||||
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-czmpuoHq-1680618008643)(./assets/2fa7ea48c428fc4689c08a49bc51d0d.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pGz8bOk7-1680618008643)(./assets/%E6%B1%82%E8%81%8C%E8%A7%92%E8%89%B2%E5%8A%9F%E8%83%BD.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UA7IUc8v-1680618008644)(./assets/%E6%8B%9B%E8%81%98%E8%80%85%E5%8A%9F%E8%83%BD.png)]
sql 优化是一个大家都比较关注的热门话题,无论你在面试,还是工作中,都很有可能会遇到,如果某天你负责的某个线上接口,出现了性能问题,需要做优化。那么你首先想到的很有可能是优化sql语句,因为它的改造成本相对于代码来说也要小得多
很多时候,我们写 sql 语句时,为了方便,喜欢直接使用select *
,一次性查出表中所有列的数据
反例:
select * from user where id=1;
在实际业务场景中,可能我们真正需要使用的只有其中一两列。查了很多数据,但是不用,白白浪费了数据库资源,比如:内存或者cpu。
此外,多查出来的数据,通过网络IO传输的过程中,也会增加数据传输的时间。
还有一个最重要的问题是:select *
不会走覆盖索引
,会出现大量的回表
操作,而从导致查询sql的性能很低。
那么,如何优化呢?
正例:
select name,age from user where id=1;
sql语句查询时,只查需要用到的列,多余的列根本无需查出来。
我们都知道sql语句使用union
关键字后,可以获取排重后的数据。
而如果使用union all
关键字,可以获取所有数据,包含重复的数据。
反例:
(select * from user where id=1)
union
(select * from user where id=2);
排重的过程需要遍历、排序和比较,它更耗时,更消耗cpu资源。
所以如果能用union all的时候,尽量不用union。
正例:
(select * from user where id=1)
union all
(select * from user where id=2);
除非是有些特殊的场景,比如union all之后,结果集中出现了重复数据,而业务场景中是不允许产生重复数据的,这时可以使用union。
小表驱动大表,也就是说用小表的数据集驱动大表的数据集。
假如有order和user两张表,其中order表有10000条数据,而user表有100条数据。
这时如果想查一下,所有有效的用户下过的订单列表。
可以使用in
关键字实现:
select * from order
where user_id in (select id from user where status=1)
也可以使用exists
关键字实现:
select * from order
where exists (select 1 from user where order.user_id = user.id and status=1)
前面提到的这种业务场景,使用in关键字去实现业务需求,更加合适。
为什么呢?
因为如果sql语句中包含了in关键字,则它会优先执行in里面的子查询语句
,然后再执行in外面的语句。如果in里面的数据量很少,作为条件查询速度更快。
而如果sql语句中包含了exists关键字,它优先执行exists左边的语句(即主查询语句)。然后把它作为条件,去跟右边的语句匹配。如果匹配上,则可以查询出数据。如果匹配不上,数据就被过滤掉了。
这个需求中,order表有10000条数据,而user表有100条数据。order表是大表,user表是小表。如果order表在左边,则用in关键字性能更好。
总结一下:
in
适用于左边大表,右边小表。exists
适用于左边小表,右边大表。不管是用in,还是exists关键字,其核心思想都是用小表驱动大表。
如果你有一批数据经过业务处理之后,需要插入数据,该怎么办?
反例:
for(Order order: list){
orderMapper.insert(order):
}
在循环中逐条插入数据。
insert into order(id,code,user_id)
values(123,'001',100);
该操作需要多次请求数据库,才能完成这批数据的插入。
但众所周知,我们在代码中,每次远程请求数据库,是会消耗一定性能的。而如果我们的代码需要请求多次数据库,才能完成本次业务功能,势必会消耗更多的性能。
那么如何优化呢?
正例:
orderMapper.insertBatch(list):
提供一个批量插入数据的方法。
insert into order(id,code,user_id)
values(123,'001',100),(124,'002',100),(125,'003',101);
这样只需要远程请求一次数据库,sql性能会得到提升,数据量越多,提升越大。
但需要注意的是,不建议一次批量操作太多的数据,如果数据太多数据库响应也会很慢。批量操作需要把握一个度,建议每批数据尽量控制在500以内。如果数据多于500,则分多批次处理。
有时候,我们需要查询某些数据中的第一条,比如:查询某个用户下的第一个订单,想看看他第一次的首单时间。
反例:
select id, create_date
from order
where user_id=123
order by create_date asc;
根据用户id查询订单,按下单时间排序,先查出该用户所有的订单数据,得到一个订单集合。然后在代码中,获取第一个元素的数据,即首单的数据,就能获取首单时间。
List list = orderMapper.getOrderList();
Order order = list.get(0);
虽说这种做法在功能上没有问题,但它的效率非常不高,需要先查询出所有的数据,有点浪费资源。
那么,如何优化呢?
正例:
select id, create_date
from order
where user_id=123
order by create_date asc
limit 1;
使用limit 1
,只返回该用户下单时间最小的那一条数据即可。
此外,在删除或者修改数据时,为了防止误操作,导致删除或修改了不相干的数据,也可以在sql语句最后加上limit。
例如:
update order set status=0,edit_time=now(3)
where id>=100 and id<200 limit 100;
这样即使误操作,比如把id搞错了,也不会对太多的数据造成影响。
对于批量查询接口,我们通常会使用in
关键字过滤出数据。比如:想通过指定的一些id,批量查询出用户信息。
sql语句如下:
select id,name from category
where id in (1,2,3...100000000);
如果我们不做任何限制,该查询语句一次性可能会查询出非常多的数据,很容易导致接口超时。
这时该怎么办呢?
select id,name from category
where id in (1,2,3...100)
limit 500;
可以在sql中对数据用limit做限制。
不过我们更多的是要在业务代码中加限制,伪代码如下:
public List getCategory(List ids) {
if(CollectionUtils.isEmpty(ids)) {
return null;
}
if(ids.size() > 500) {
throw new BusinessException("一次最多允许查询500条记录")
}
return mapper.getCategoryList(ids);
}
还有一个方案就是:如果ids超过500条记录,可以分批用多线程去查询数据。每批只查500条记录,最后把查询到的数据汇总到一起返回。
不过这只是一个临时方案,不适合于ids实在太多的场景。因为ids太多,即使能快速查出数据,但如果返回的数据量太大了,网络传输也是非常消耗性能的,接口性能始终好不到哪里去。
有时候,我们需要通过远程接口查询数据,然后同步到另外一个数据库。
反例:
select * from user;
如果直接获取所有的数据,然后同步过去。这样虽说非常方便,但是带来了一个非常大的问题,就是如果数据很多的话,查询性能会非常差。
这时该怎么办呢?
正例:
select * from user
where id>#{lastId} and create_time >= #{lastCreateTime}
limit 100;
按id和时间升序,每次只同步一批数据,这一批数据只有100条记录。每次同步完成之后,保存这100条数据中最大的id和时间,给同步下一批数据的时候用。
通过这种增量查询的方式,能够提升单次查询的效率。
有时候,列表页在查询数据时,为了避免一次性返回过多的数据影响接口性能,我们一般会对查询接口做分页处理。
在mysql中分页一般用的limit
关键字:
select id,name,age
from user limit 10,20;
如果表中数据量少,用limit关键字做分页,没啥问题。但如果表中数据量很多,用它就会出现性能问题。
比如现在分页参数变成了:
select id,name,age
from user limit 1000000,20;
mysql会查到1000020条数据,然后丢弃前面的1000000条,只查后面的20条数据,这个是非常浪费资源的。
那么,这种海量数据该怎么分页呢?
优化sql:
select id,name,age
from user where id > 1000000 limit 20;
先找到上次分页最大的id,然后利用id上的索引查询。不过该方案,要求id是连续的,并且有序的。
还能使用between
优化分页。
select id,name,age
from user where id between 1000000 and 1000020;
需要注意的是between要在唯一索引上分页,不然会出现每页大小不一致的问题。
mysql中如果需要从两张以上的表中查询出数据的话,一般有两种实现方式:子查询
和 连接查询
。
子查询的例子如下:
select * from order
where user_id in (select id from user where status=1)
子查询语句可以通过in
关键字实现,一个查询语句的条件落在另一个select语句的查询结果中。程序先运行在嵌套在最内层的语句,再运行外层的语句。
子查询语句的优点是简单,结构化,如果涉及的表数量不多的话。
但缺点是mysql执行子查询时,需要创建临时表,查询完毕后,需要再删除这些临时表,有一些额外的性能消耗。
这时可以改成连接查询。具体例子如下:
select o.* from order o
inner join user u on o.user_id = u.id
where u.status=1
根据阿里巴巴开发者手册的规定,join表的数量不应该超过3
个。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cIioZ8AH-1680618008644)(./assets/image-20230404163651198.png)]
反例:
select a.name,b.name.c.name,d.name
from a
inner join b on a.id = b.a_id
inner join c on c.b_id = b.id
inner join d on d.c_id = c.id
inner join e on e.d_id = d.id
inner join f on f.e_id = e.id
inner join g on g.f_id = f.id
如果join太多,mysql在选择索引的时候会非常复杂,很容易选错索引。
并且如果没有命中中,nested loop join 就是分别从两个表读一行数据进行两两对比,复杂度是 n^2。
所以我们应该尽量控制join表的数量。
正例:
select a.name,b.name.c.name,a.d_name
from a
inner join b on a.id = b.a_id
inner join c on c.b_id = b.id
如果实现业务场景中需要查询出另外几张表中的数据,可以在a、b、c表中冗余专门的字段
,比如:在表a中冗余d_name字段,保存需要查询出的数据。
不过我之前也见过有些ERP系统,并发量不大,但业务比较复杂,需要join十几张表才能查询出数据。
所以join表的数量要根据系统的实际情况决定,不能一概而论,尽量越少越好。
我们在涉及到多张表联合查询的时候,一般会使用join
关键字。
而join使用最多的是left join和inner join。
left join
:求两个表的交集外加左表剩下的数据。inner join
:求两个表交集的数据。使用inner join的示例如下:
select o.id,o.code,u.name
from order o
inner join user u on o.user_id = u.id
where u.status=1;
如果两张表使用inner join关联,mysql会自动选择两张表中的小表,去驱动大表,所以性能上不会有太大的问题。
使用left join的示例如下:
select o.id,o.code,u.name
from order o
left join user u on o.user_id = u.id
where u.status=1;
如果两张表使用left join关联,mysql会默认用left join关键字左边的表,去驱动它右边的表。如果左边的表数据很多时,就会出现性能问题。
要特别注意的是在用left join关联查询时,左边要用小表,右边可以用大表。如果能用inner join的地方,尽量少用left join。
众所周知,索引能够显著的提升查询sql的性能,但索引数量并非越多越好。
因为表中新增数据时,需要同时为它创建索引,而索引是需要额外的存储空间的,而且还会有一定的性能消耗。
阿里巴巴的开发者手册中规定,单表的索引数量应该尽量控制在5
个以内,并且单个索引中的字段数不超过5
个。
mysql使用的B+树的结构来保存索引的,在insert、update和delete操作时,需要更新B+树索引。如果索引过多,会消耗很多额外的性能。
那么,问题来了,如果表中的索引太多,超过了5个该怎么办?
这个问题要辩证的看,如果你的系统并发量不高,表中的数据量也不多,其实超过5个也可以,只要不要超过太多就行。
但对于一些高并发的系统,请务必遵守单表索引数量不要超过5的限制。
那么,高并发系统如何优化索引数量?
能够建联合索引,就别建单个索引,可以删除无用的单个索引。
将部分查询功能迁移到其他类型的数据库中,比如:Elastic Seach、HBase等,在业务表中只需要建几个关键索引即可。
char
表示固定字符串类型,该类型的字段存储空间的固定的,会浪费存储空间。
alter table order
add column code char(20) NOT NULL;
varchar
表示变长字符串类型,该类型的字段存储空间会根据实际数据的长度调整,不会浪费存储空间。
alter table order
add column code varchar(20) NOT NULL;
如果是长度固定的字段,比如用户手机号,一般都是11位的,可以定义成char类型,长度是11字节。
但如果是企业名称字段,假如定义成char类型,就有问题了。
如果长度定义得太长,比如定义成了200字节,而实际企业长度只有50字节,则会浪费150字节的存储空间。
如果长度定义得太短,比如定义成了50字节,但实际企业名称有100字节,就会存储不下,而抛出异常。
所以建议将企业名称改成varchar类型,变长字段存储空间小,可以节省存储空间,而且对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
我们在选择字段类型时,应该遵循这样的原则:
还有很多原则,这里就不一一列举了。
我们有很多业务场景需要使用group by
关键字,它主要的功能是去重和分组。
通常它会跟having
一起配合使用,表示分组后再根据一定的条件过滤数据。
反例:
select user_id,user_name from order
group by user_id
having user_id <= 200;
这种写法性能不好,它先把所有的订单根据用户id分组之后,再去过滤用户id大于等于200的用户。
分组是一个相对耗时的操作,为什么我们不先缩小数据的范围之后,再分组呢?
正例:
select user_id,user_name from order
where user_id <= 200
group by user_id
使用where条件在分组前,就把多余的数据过滤掉了,这样分组时效率就会更高一些。
其实这是一种思路,不仅限于group by的优化。我们的sql语句在做一些耗时的操作之前,应尽可能缩小数据范围,这样能提升sql整体的性能。
sql优化当中,有一个非常重要的内容就是:索引优化
。
很多时候sql语句,走了索引,和没有走索引,执行效率差别很大。所以索引优化被作为sql优化的首选。
索引优化的第一步是:检查sql语句有没有走索引。
那么,如何查看sql走了索引没?
可以使用explain
命令,查看mysql的执行计划。
例如:
explain select * from `order` where code='002';
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oz8xCAWI-1680618008644)(./assets/640.png)]
通过这几列可以判断索引使用情况,执行计划包含列的含义如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e7KGNE0v-1680618008645)(./assets/640-1680509934027-1.png)]
说实话,sql语句没有走索引,排除没有建索引之外,最大的可能性是索引失效了。
下面说说索引失效的常见原因:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KWLr5oXZ-1680618008645)(./assets/640-1680509934027-2.png)]
如果不是上面的这些原因,则需要再进一步排查一下其他原因。
此外,你有没有遇到过这样一种情况:明明是同一条sql,只有入参不同而已。有的时候走的索引a,有的时候却走的索引b?
没错,有时候mysql会选错索引。
必要时可以使用force index
来强制查询sql走某个索引。
hospital
/*
Navicat Premium Data Transfer
Source Server : MySQL
Source Server Type : MySQL
Source Server Version : 50730
Source Host : localhost:3306
Source Schema : hospital
Target Server Type : MySQL
Target Server Version : 50730
File Encoding : 65001
Date: 04/08/2021 09:00:32
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for checkitem
-- ----------------------------
DROP TABLE IF EXISTS `checkitem`;
CREATE TABLE `checkitem` (
`checkItemID` int(4) NOT NULL AUTO_INCREMENT COMMENT '检查项目编号',
`checkItemName` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '检查项目名称',
`checkItemCost` float NOT NULL COMMENT '检查项目价格',
PRIMARY KEY (`checkItemID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '检查项目表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of checkitem
-- ----------------------------
INSERT INTO `checkitem` VALUES (1, '血常规', 28);
INSERT INTO `checkitem` VALUES (2, '尿常规', 20);
INSERT INTO `checkitem` VALUES (3, '血脂、血糖检查', 25);
INSERT INTO `checkitem` VALUES (4, '凝血五项', 50);
INSERT INTO `checkitem` VALUES (5, '肺炎支、衣原体(快速)', 66);
INSERT INTO `checkitem` VALUES (6, 'CT', 110);
-- ----------------------------
-- Table structure for department
-- ----------------------------
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`depID` int(4) NOT NULL AUTO_INCREMENT COMMENT '科室编号',
`depName` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '科室名称',
PRIMARY KEY (`depID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '科室表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of department
-- ----------------------------
INSERT INTO `department` VALUES (1, '急诊科');
INSERT INTO `department` VALUES (2, '呼吸科');
INSERT INTO `department` VALUES (3, '内科');
-- ----------------------------
-- Table structure for department_checkitem
-- ----------------------------
DROP TABLE IF EXISTS `department_checkitem`;
CREATE TABLE `department_checkitem` (
`id` int(4) NOT NULL AUTO_INCREMENT COMMENT '关系编号',
`depID` int(4) NOT NULL COMMENT '科室编号',
`checkItemID` int(4) NOT NULL COMMENT '检查项目编号',
PRIMARY KEY (`id`) USING BTREE,
INDEX `fk_department_checkitem_checkitem`(`checkItemID`) USING BTREE,
INDEX `fk_department_checkitem_department`(`depID`) USING BTREE,
CONSTRAINT `fk_department_checkitem_checkitem` FOREIGN KEY (`checkItemID`) REFERENCES `checkitem` (`checkItemID`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `fk_department_checkitem_department` FOREIGN KEY (`depID`) REFERENCES `department` (`depID`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '科室可开检查项目关系表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of department_checkitem
-- ----------------------------
INSERT INTO `department_checkitem` VALUES (1, 1, 1);
INSERT INTO `department_checkitem` VALUES (2, 1, 2);
INSERT INTO `department_checkitem` VALUES (3, 2, 1);
INSERT INTO `department_checkitem` VALUES (4, 2, 5);
INSERT INTO `department_checkitem` VALUES (5, 3, 1);
INSERT INTO `department_checkitem` VALUES (6, 3, 2);
INSERT INTO `department_checkitem` VALUES (7, 3, 3);
INSERT INTO `department_checkitem` VALUES (8, 3, 4);
-- ----------------------------
-- Table structure for patient
-- ----------------------------
DROP TABLE IF EXISTS `patient`;
CREATE TABLE `patient` (
`patientID` int(4) NOT NULL AUTO_INCREMENT COMMENT '病人编号',
`password` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '登录密码',
`birthDate` date NULL DEFAULT NULL COMMENT '出生日期',
`gender` varchar(4) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '男' COMMENT '性别',
`patientName` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '病人姓名',
`phoneNum` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '联系电话',
`email` varchar(70) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
`identityNum` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '身份证号',
`address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '地址不详' COMMENT '地址',
PRIMARY KEY (`patientID`) USING BTREE,
UNIQUE INDEX `identityNum`(`identityNum`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '病人表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of patient
-- ----------------------------
INSERT INTO `patient` VALUES (1, '123456', '1985-06-07', '女', '夏颖', '13800000001', '[email protected]', '110000198506071100', '厦门市');
INSERT INTO `patient` VALUES (2, '234567', '1985-06-08', '男', '李政', '13800000002', '[email protected]', '210000198506082100', '长春市');
INSERT INTO `patient` VALUES (3, '345678', '2010-03-02', '女', '李沁', '13800000003', '[email protected]', '120000201003021200', '合肥市');
INSERT INTO `patient` VALUES (4, '456789', '1999-01-02', '女', '李思雨', '13800000004', '[email protected]', '150000199901021500', '北京市');
INSERT INTO `patient` VALUES (5, '567890', '2008-10-17', '男', '夏天', '13800000005', '[email protected]', '210000200810172100', '长春市');
INSERT INTO `patient` VALUES (6, '678901', '1999-03-08', '男', '刘占波', '13800000006', '[email protected]', '210000199903082101', '惠州市');
INSERT INTO `patient` VALUES (7, '789012', '1987-05-02', '女', '廖慧颖', '13800000007', '[email protected]', '220000198705022200', '广州市');
INSERT INTO `patient` VALUES (8, '890123', '1975-03-02', '男', '李伟忠', '13800000008', '[email protected]', '230000197503022300', '沈阳市');
INSERT INTO `patient` VALUES (9, '901234', '1986-10-11', '男', '姚维新', '13800000009', '[email protected]', '310000198610113100', '北京市');
INSERT INTO `patient` VALUES (10, '012345', '1975-03-04', '男', '陈建', '13800000010', '[email protected]', '320000197503043200', '北京市');
INSERT INTO `patient` VALUES (11, '098765', '1992-01-01', '女', '林永清', '13800000011', '[email protected]', '330000199201013300', '长春市');
INSERT INTO `patient` VALUES (12, '987654', '1993-03-02', '女', '李亚', '13800000012', '[email protected]', '340000199303023400', '保定市');
INSERT INTO `patient` VALUES (13, '909000', '1995-02-12', '女', '张菲', '13887676500', '[email protected]', '610000199502126100', '北京市');
-- ----------------------------
-- Table structure for prescription
-- ----------------------------
DROP TABLE IF EXISTS `prescription`;
CREATE TABLE `prescription` (
`examID` int(4) NOT NULL AUTO_INCREMENT COMMENT '检查编号',
`patientID` int(4) NOT NULL COMMENT '病人编号',
`depID` int(4) NOT NULL COMMENT '开处方的科室编号',
`checkResult` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '检查结果',
`checkItemID` int(4) NOT NULL COMMENT '检查项目编号',
`examDate` datetime(0) NOT NULL COMMENT '检查日期',
UNIQUE INDEX `examID`(`examID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '处方表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of prescription
-- ----------------------------
INSERT INTO `prescription` VALUES (1, 1, 1, '正常', 1, '2020-01-02 00:00:00');
INSERT INTO `prescription` VALUES (2, 1, 1, '正常', 2, '2020-01-02 00:00:00');
INSERT INTO `prescription` VALUES (3, 3, 2, '肺炎支原体阳性', 5, '2020-04-05 00:00:00');
INSERT INTO `prescription` VALUES (4, 1, 1, '正常', 1, '2020-02-06 00:00:00');
INSERT INTO `prescription` VALUES (5, 8, 3, '正常', 4, '2020-03-02 00:00:00');
INSERT INTO `prescription` VALUES (6, 8, 3, '血糖偏高', 3, '2020-03-02 00:00:00');
INSERT INTO `prescription` VALUES (7, 8, 3, '正常', 1, '2020-03-02 00:00:00');
INSERT INTO `prescription` VALUES (8, 10, 3, '正常', 3, '2020-03-02 00:00:00');
INSERT INTO `prescription` VALUES (9, 1, 1, '白细胞数量偏高', 1, '2020-07-08 00:00:00');
-- ----------------------------
-- View structure for v_patient_cost
-- ----------------------------
DROP VIEW IF EXISTS `v_patient_cost`;
CREATE ALGORITHM = UNDEFINED DEFINER = `root`@`localhost` SQL SECURITY DEFINER VIEW `v_patient_cost` AS select `p1`.`patientID` AS `patientID`,`patient`.`patientName` AS `patientName`,sum(`checkitem`.`checkItemCost`) AS `totalcost` from ((`prescription` `p1` join `checkitem` on((`p1`.`checkItemID` = `checkitem`.`checkItemID`))) join `patient` on((`p1`.`patientID` = `patient`.`patientID`))) group by `p1`.`patientID`;
-- ----------------------------
-- View structure for v_prescription
-- ----------------------------
DROP VIEW IF EXISTS `v_prescription`;
CREATE ALGORITHM = UNDEFINED DEFINER = `root`@`localhost` SQL SECURITY DEFINER VIEW `v_prescription` AS select `pa`.`patientName` AS `姓名`,`pa`.`gender` AS `性别`,`pa`.`birthDate` AS `年龄`,`c`.`checkItemName` AS `检查项目`,`pr`.`checkResult` AS `检查结果`,`d`.`depName` AS `检查科室`,`pr`.`examDate` AS `检查日期` from (((`prescription` `pr` join `patient` `pa` on((`pr`.`patientID` = `pa`.`patientID`))) join `department` `d` on((`pr`.`depID` = `d`.`depID`))) join `checkitem` `c` on((`pr`.`checkItemID` = `c`.`checkItemID`)));
-- ----------------------------
-- Procedure structure for LOOPLoopProc
-- ----------------------------
DROP PROCEDURE IF EXISTS `LOOPLoopProc`;
delimiter ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `LOOPLoopProc`(OUT patientNum INT)
BEGIN
DECLARE x INT;
DECLARE str VARCHAR(255);
SET x = 1;
SET str = '';
loop_label: LOOP
IF x > 10 THEN
LEAVE loop_label;
END IF;
SET x = x + 1;
IF (x mod 2) THEN
ITERATE loop_label;
ELSE
SET str = CONCAT(str,x,',');
END IF;
END LOOP;
SELECT str;
END
;;
delimiter ;
-- ----------------------------
-- Procedure structure for proc1
-- ----------------------------
DROP PROCEDURE IF EXISTS `proc1`;
delimiter ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc1`( )
BEGIN
SET @name = '王明';
END
;;
delimiter ;
-- ----------------------------
-- Procedure structure for proc2
-- ----------------------------
DROP PROCEDURE IF EXISTS `proc2`;
delimiter ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc2`( )
BEGIN
SELECT CONCAT('name:',@name);
END
;;
delimiter ;
-- ----------------------------
-- Procedure structure for proc_calc_patient_avg_cost
-- ----------------------------
DROP PROCEDURE IF EXISTS `proc_calc_patient_avg_cost`;
delimiter ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_calc_patient_avg_cost`( IN p_patientID INT, OUT avg_cost DOUBLE)
BEGIN
DECLARE t_totalcost FLOAT; #病人总的医疗费用
DECLARE t_visit_count INT; #病人来看病的次数
#根据病人编号统计病人总的医疗费用和看病次数并将结果报错到局部变量中
SELECT SUM(checkItemCost) INTO t_totalcost FROM prescription p1 INNER JOIN checkitem ON p1.checkItemID = checkitem.checkItemID WHERE patientID = p_patientID;
SELECT COUNT(DISTINCT examDate) INTO t_visit_count FROM prescription WHERE patientID = p_patientID;
#计算病人每次就医的平均费用
SET avg_cost = t_totalcost/t_visit_count;
END
;;
delimiter ;
-- ----------------------------
-- Procedure structure for proc_checkitem_insert
-- ----------------------------
DROP PROCEDURE IF EXISTS `proc_checkitem_insert`;
delimiter ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_checkitem_insert`( IN checkitems VARCHAR(100))
BEGIN
DECLARE comma_pos INT;
DECLARE current_checkitem VARCHAR(20);
loop_label: LOOP
SET comma_pos = LOCATE(',', checkitems);
SET current_checkitem = SUBSTR(checkitems, 1, comma_pos-1);
IF current_checkitem <> '' THEN
SET checkitems = SUBSTR(checkitems, comma_pos+1);
ELSE
SET current_checkitem = checkitems;
END IF;
INSERT INTO checkitem(checkItemName,checkItemCost) VALUES(current_checkitem,70);
IF comma_pos=0 OR current_checkitem='' THEN
LEAVE loop_label;
END IF;
END LOOP loop_label;
END
;;
delimiter ;
-- ----------------------------
-- Procedure structure for proc_checkitem_patients
-- ----------------------------
DROP PROCEDURE IF EXISTS `proc_checkitem_patients`;
delimiter ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_checkitem_patients`( IN item_name VARCHAR(100))
BEGIN
SELECT patientName FROM patient WHERE patientID IN(
SELECT patientID FROM prescription WHERE checkItemID IN(
SELECT checkItemID FROM checkitem WHERE checkItemName LIKE CONCAT('%',item_name,'%')));
END
;;
delimiter ;
-- ----------------------------
-- Procedure structure for proc_exam_GetLastExamDateByPatientNameAndDepID
-- ----------------------------
DROP PROCEDURE IF EXISTS `proc_exam_GetLastExamDateByPatientNameAndDepID`;
delimiter ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_exam_GetLastExamDateByPatientNameAndDepID`(IN patient_name VARCHAR(50), IN dep_id INT,OUT last_exam_date DATETIME)
BEGIN
#Routine body goes here...
DECLARE patient_id INT; #声明局部变量
SELECT patientID INTO patient_id FROM patient WHERE patientName= patient_name;
SELECT patient_id; #输出病人的ID
SELECT MAX(examDate) INTO last_exam_date FROM prescription WHERE patientID = patient_id AND depID = dep_id;
END
;;
delimiter ;
-- ----------------------------
-- Procedure structure for proc_income_calSubsidy
-- ----------------------------
DROP PROCEDURE IF EXISTS `proc_income_calSubsidy`;
delimiter ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_income_calSubsidy`(IN i_patientID INT ,IN i_year VARCHAR(10), OUT o_subsidy FLOAT)
BEGIN
DECLARE t_totalCost Float;
DECLARE t_income Float DEFAULT -1;
SELECT SUM(checkItemCost) INTO t_totalCost FROM prescription p1 INNER JOIN checkitem ON p1.checkItemID = checkitem.checkItemID
WHERE patientID = 1 AND examDate >= CONCAT(i_year,'-01-01') AND examDate <= CONCAT(i_year,'-12-31');
SELECT income INTO t_income FROM subsidy WHERE patientID = i_patientID;
#根据规则计算返还金额
CASE
WHEN t_income >=0 AND t_income < 5000 THEN SET o_subsidy = t_totalCost * 0.2;
WHEN t_income < 1000 THEN SET o_subsidy = t_totalCost * 0.15;
WHEN t_income < 30000 THEN SET o_subsidy = t_totalCost * 0.05;
WHEN t_income >= 30000 OR t_income < 0 THEN SET o_subsidy = 0;
END CASE;
END
;;
delimiter ;
-- ----------------------------
-- Procedure structure for proc_patient_countPatient
-- ----------------------------
DROP PROCEDURE IF EXISTS `proc_patient_countPatient`;
delimiter ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_patient_countPatient`()
SQL SECURITY INVOKER
BEGIN
SELECT COUNT(*) FROM patient;
END
;;
delimiter ;
-- ----------------------------
-- Procedure structure for proc_patient_countPatient2
-- ----------------------------
DROP PROCEDURE IF EXISTS `proc_patient_countPatient2`;
delimiter ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_patient_countPatient2`(OUT patientNum INT)
BEGIN
SELECT COUNT(*) INTO patientNum FROM patient;
END
;;
delimiter ;
SET FOREIGN_KEY_CHECKS = 1;
mySchool
#先确定数据库是否存在
DROP DATABASE IF EXISTS myschool;
CREATE DATABASE myschool; #创建数据库
SHOW DATABASES; #查看数据库
USE myschool; #选择已存在的数据库
#先确定表是否存在
DROP TABLE IF EXISTS student;
#创建学生表
CREATE TABLE student(
studentNo INT(4) NOT NULL COMMENT '学号',
loginPwd VARCHAR(20) NOT NULL COMMENT '密码',
studentName VARCHAR(50) NOT NULL COMMENT '学生姓名',
sex CHAR(2) DEFAULT '男' NOT NULL COMMENT '性别',
gradeId INT(4) UNSIGNED COMMENT '年级编号',
phone VARCHAR(50) COMMENT '联系电话',
address VARCHAR(255) DEFAULT '地址不详' COMMENT '地址',
bornDate DATETIME COMMENT '出生时间',
email VARCHAR(50) COMMENT '邮件账号',
identityCard VARCHAR(18) UNIQUE COMMENT'身份证号码',
PRIMARY KEY(studentNo)
)COMMENT '学生表';
#如果subject表已存在,则删除
DROP TABLE IF EXISTS subject;#创建课程表
CREATE TABLE subject(
subjectNo INT(4) NOT NULL AUTO_INCREMENT COMMENT '课程编号',
subjectName VARCHAR(50) COMMENT '课程名称',
classHour INT(4) COMMENT '学时',
gradeID INT(4) COMMENT '年级编号' ,
PRIMARY KEY (subjectNo)
)COMMENT '课程表';
#如果result表已存在,则删除
DROP TABLE IF EXISTS result;
#创建result表
CREATE TABLE result(
studentNo INT(4) NOT NULL COMMENT '学号',
subjectNo INT(4) NOT NULL COMMENT '课程编号',
examDate DATETIME NOT NULL COMMENT '考试日期',
studentResult INT(4) COMMENT '考试成绩' NOT NULL
)COMMENT '成绩表';
#如果grade表已存在,则删除
DROP TABLE IF EXISTS grade;
#创建年级表
CREATE TABLE grade (
gradeID INT(4) NOT NULL AUTO_INCREMENT COMMENT '年级编号',
gradeName VARCHAR(50) NOT NULL COMMENT '年级名称' ,
PRIMARY KEY (gradeID)
)COMMENT '年级表';
SHOW TABLES;
添加数据
#为年级表添加数据
INSERT INTO grade(gradeID, gradeName) VALUES (1, 'Y1');
INSERT INTO grade(gradeID, gradeName) VALUES (2, 'Y2');
INSERT INTO grade(gradeID, gradeName) VALUES (3, 'Y3');
#为科目表添加数据
INSERT INTO subject(subjectName,classHour,gradeId) VALUES('Logic Java',220,1),('HTML',160,1),('Java OOP',230,2);
/*学生表数据*/
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('10000','012345','郭靖','男','1','13645667783','天津市河西区','1990-09-08 00:00:00',NULL,NULL);
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('10001','123456','李文才','男','1','13645667890','地址不详','1994-04-12 00:00:00',NULL,NULL);
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('10002','234567','李斯文','男','1','13645556793','河南洛阳','1993-07-23 00:00:00',NULL,NULL);
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('10003','345678','张萍','女','1','13642345112','地址不详','1995-06-10 00:00:00',NULL,NULL);
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('10004','456789','韩秋洁','女','1','13812344566','北京市海淀区','1995-07-15 00:00:00',NULL,NULL);
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('10005','567890','张秋丽','女','1','13567893246','北京市东城区','1994-01-17 00:00:00',NULL,NULL);
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('10006','678901','肖梅','女','1','13563456721','河北省石家庄市','1991-02-17 00:00:00',NULL,NULL);
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('10007','789012','秦洋','男','1','13056434411','上海市卢湾区','1992-04-18 00:00:00',NULL,NULL);
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('10008','890123','何睛睛','女','1','13053445221','广州市天河区','1997-07-23 00:00:00',NULL,NULL);
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('20000','901234','王宝宝','男','2','15076552323','地址不详','1996-06-05 00:00:00',NULL,NULL);
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('20010','012345','何小华','女','2','13318877954','地址不详','1995-09-10 00:00:00',NULL,NULL);
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('30011','098765','陈志强','男','3','13689965430','地址不详','1994-09-27 00:00:00',NULL,NULL);
insert into student (studentNo, loginPwd, studentName, sex, gradeId, phone, address, bornDate, email, identityCard) values('30012','987654','李露露','女','3','13685678854','地址不详','1992-09-27 00:00:00',NULL,NULL);
/*成绩表数据*/
insert into result (studentNo, subjectNo, examDate, studentResult) values('10000','1','2019-02-17 00:00:00','71');
insert into result (studentNo, subjectNo, examDate, studentResult) values('10001','1','2019-02-17 00:00:00','46');
insert into result (studentNo, subjectNo, examDate, studentResult) values('10002','1','2019-02-17 00:00:00','83');
insert into result (studentNo, subjectNo, examDate, studentResult) values('10003','1','2019-02-17 00:00:00','60');
insert into result (studentNo, subjectNo, examDate, studentResult) values('10004','1','2019-02-17 00:00:00','60');
insert into result (studentNo, subjectNo, examDate, studentResult) values('10005','1','2019-02-17 00:00:00','95');
insert into result (studentNo, subjectNo, examDate, studentResult) values('10006','1','2019-02-17 00:00:00','93');
insert into result (studentNo, subjectNo, examDate, studentResult) values('10007','1','2019-02-17 00:00:00','23');
select * from grade;
select * from subject;
select * from student;
select * from result;
database.properties
driver=com.mysql.jdbc.Driver
url=jdbc\:mysql\://localhost\:3306/hospital?useUnicode\=true&characterEncoding\=UTF-8&serverTimezone\=Asia/Shanghai
user=root
password=root
BaseDao.java
package xuanzishare.dao;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class BaseDao {
public static String driver;
// 数据库驱动
public static String url;
// url
public static String user;
// 数据库用户名
public static String password;
// 数据库密码
static {
//静态代码块,在类加载的时候执行
init();
}
public static void init() {
String configFile = "database.properties";
//配置文件路径
//加载配置文件到输入流中
InputStream is = BaseDao.class.getClassLoader().getResourceAsStream(configFile);
Properties params = null;
try {
params = new Properties();
//从输入流中读取属性列表
params.load(is);
} catch (IOException e) {
e.printStackTrace();
}
//根据指定的获取对应的值
driver = params.getProperty("driver");
url = params.getProperty("url");
user = params.getProperty("user");
password = params.getProperty("password");
}
/**
* 得到数据库连接
*
* @return 数据库连接
*/
public static Connection getConn() {
Connection conn = null;
try {
Class.forName(driver);
// 注册驱动
conn = DriverManager.getConnection(url, user, password);
// 获得数据库连接
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
return conn;
// 返回连接
}
public static void closeAll(Connection conn, PreparedStatement ps, ResultSet rs) {
/* 如果rs不空,关闭rs */
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
/* 如果pstmt不空,关闭pstmt */
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
/* 如果conn不空,关闭conn */
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public int updateBuffer(String sql, Object[] objects) {
Connection conn = null;
PreparedStatement ps = null;
int i = 0;
try {
conn = getConn();
ps = conn.prepareStatement(sql);
if (objects != null) {
for (int j = 0; j < objects.length; j++) {
ps.setObject(j + 1, objects[j]);
}
}
System.out.println("\u001B[34mupdateBuffer:" + ps + "\u001B[0m");
i = ps.executeUpdate();
} catch (Exception e) {
throw new RuntimeException(e);
}
closeAll(conn, ps, null);
return i;
}
public double selectAllId(String sql, Object[] objects) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
double allId = 0;
try {
conn = getConn();
ps = conn.prepareStatement(sql);
if (objects != null) {
for (int i = 0; i < objects.length; i++) {
ps.setObject(i + 1, objects[i]);
}
}
System.out.println("\u001B[34mselectAllId:" + ps + "\u001B[0m");
rs = ps.executeQuery();
allId = 0;
while (rs.next()) {
allId = rs.getDouble(1);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
closeAll(conn, ps, rs);
return allId;
}
// 只查询数字类型 把值返还回去
}
函数 | 描述 | 实例 |
---|---|---|
ASCII(s) | 返回字符串 s 的第一个字符的 ASCII 码。 | 返回 CustomerName 字段第一个字母的 ASCII 码:SELECT ASCII(CustomerName) AS NumCodeOfFirstChar FROM Customers; |
CHAR_LENGTH(s) | 返回字符串 s 的字符数 | 返回字符串 RUNOOB 的字符数SELECT CHAR_LENGTH("RUNOOB") AS LengthOfString; |
CHARACTER_LENGTH(s) | 返回字符串 s 的字符数,等同于 CHAR_LENGTH(s) | 返回字符串 RUNOOB 的字符数SELECT CHARACTER_LENGTH("RUNOOB") AS LengthOfString; |
CONCAT(s1,s2…sn) | 字符串 s1,s2 等多个字符串合并为一个字符串 | 合并多个字符串SELECT CONCAT("SQL ", "Runoob ", "Gooogle ", "Facebook") AS ConcatenatedString; |
CONCAT_WS(x, s1,s2…sn) | 同 CONCAT(s1,s2,…) 函数,但是每个字符串之间要加上 x,x 可以是分隔符 | 合并多个字符串,并添加分隔符:SELECT CONCAT_WS("-", "SQL", "Tutorial", "is", "fun!")AS ConcatenatedString; |
FIELD(s,s1,s2…) | 返回第一个字符串 s 在字符串列表(s1,s2…)中的位置 | 返回字符串 c 在列表值中的位置:SELECT FIELD("c", "a", "b", "c", "d", "e"); |
FIND_IN_SET(s1,s2) | 返回在字符串s2中与s1匹配的字符串的位置 | 返回字符串 c 在指定字符串中的位置:SELECT FIND_IN_SET("c", "a,b,c,d,e"); |
FORMAT(x,n) | 函数可以将数字 x 进行格式化 “#,###.##”, 将 x 保留到小数点后 n 位,最后一位四舍五入。 | 格式化数字 “#,###.##” 形式:SELECT FORMAT(250500.5634, 2); -- 输出 250,500.56 |
INSERT(s1,x,len,s2) | 字符串 s2 替换 s1 的 x 位置开始长度为 len 的字符串 | 从字符串第一个位置开始的 6 个字符替换为 runoob:SELECT INSERT("google.com", 1, 6, "runoob"); -- 输出:runoob.com |
LOCATE(s1,s) | 从字符串 s 中获取 s1 的开始位置 | 获取 b 在字符串 abc 中的位置:SELECT LOCATE('st','myteststring'); -- 5 返回字符串 abc 中 b 的位置:SELECT LOCATE('b', 'abc') -- 2 |
LCASE(s) | 将字符串 s 的所有字母变成小写字母 | 字符串 RUNOOB 转换为小写:SELECT LCASE('RUNOOB') -- runoob |
LEFT(s,n) | 返回字符串 s 的前 n 个字符 | 返回字符串 runoob 中的前两个字符:SELECT LEFT('runoob',2) -- ru |
LOWER(s) | 将字符串 s 的所有字母变成小写字母 | 字符串 RUNOOB 转换为小写:SELECT LOWER('RUNOOB') -- runoob |
LPAD(s1,len,s2) | 在字符串 s1 的开始处填充字符串 s2,使字符串长度达到 len | 将字符串 xx 填充到 abc 字符串的开始处:SELECT LPAD('abc',5,'xx') -- xxabc |
LTRIM(s) | 去掉字符串 s 开始处的空格 | 去掉字符串 RUNOOB开始处的空格:SELECT LTRIM(" RUNOOB") AS LeftTrimmedString;-- RUNOOB |
MID(s,n,len) | 从字符串 s 的 n 位置截取长度为 len 的子字符串,同 SUBSTRING(s,n,len) | 从字符串 RUNOOB 中的第 2 个位置截取 3个 字符:SELECT MID("RUNOOB", 2, 3) AS ExtractString; -- UNO |
POSITION(s1 IN s) | 从字符串 s 中获取 s1 的开始位置 | 返回字符串 abc 中 b 的位置:SELECT POSITION('b' in 'abc') -- 2 |
REPEAT(s,n) | 将字符串 s 重复 n 次 | 将字符串 runoob 重复三次:SELECT REPEAT('runoob',3) -- runoobrunoobrunoob |
REPLACE(s,s1,s2) | 将字符串 s2 替代字符串 s 中的字符串 s1 | 将字符串 abc 中的字符 a 替换为字符 x:SELECT REPLACE('abc','a','x') --xbc |
REVERSE(s) | 将字符串s的顺序反过来 | 将字符串 abc 的顺序反过来:SELECT REVERSE('abc') -- cba |
RIGHT(s,n) | 返回字符串 s 的后 n 个字符 | 返回字符串 runoob 的后两个字符:SELECT RIGHT('runoob',2) -- ob |
RPAD(s1,len,s2) | 在字符串 s1 的结尾处添加字符串 s2,使字符串的长度达到 len | 将字符串 xx 填充到 abc 字符串的结尾处:SELECT RPAD('abc',5,'xx') -- abcxx |
RTRIM(s) | 去掉字符串 s 结尾处的空格 | 去掉字符串 RUNOOB 的末尾空格:SELECT RTRIM("RUNOOB ") AS RightTrimmedString; -- RUNOOB |
SPACE(n) | 返回 n 个空格 | 返回 10 个空格:SELECT SPACE(10); |
STRCMP(s1,s2) | 比较字符串 s1 和 s2,如果 s1 与 s2 相等返回 0 ,如果 s1>s2 返回 1,如果 s1比较字符串: |
SELECT STRCMP("runoob", "runoob"); -- 0 |
SUBSTR(s, start, length) | 从字符串 s 的 start 位置截取长度为 length 的子字符串 | 从字符串 RUNOOB 中的第 2 个位置截取 3个 字符:SELECT SUBSTR("RUNOOB", 2, 3) AS ExtractString; -- UNO |
SUBSTRING(s, start, length) | 从字符串 s 的 start 位置截取长度为 length 的子字符串,等同于 SUBSTR(s, start, length) | 从字符串 RUNOOB 中的第 2 个位置截取 3个 字符:SELECT SUBSTRING("RUNOOB", 2, 3) AS ExtractString; -- UNO |
SUBSTRING_INDEX(s, delimiter, number) | 返回从字符串 s 的第 number 个出现的分隔符 delimiter 之后的子串。 如果 number 是正数,返回第 number 个字符左边的字符串。 如果 number 是负数,返回第(number 的绝对值(从右边数))个字符右边的字符串。 | SELECT SUBSTRING_INDEX('a*b','*',1) -- a SELECT SUBSTRING_INDEX('a*b','*',-1) -- b SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('a*b*c*d*e','*',3),'*',-1) -- c |
TRIM(s) | 去掉字符串 s 开始和结尾处的空格 | 去掉字符串 RUNOOB 的首尾空格:SELECT TRIM(' RUNOOB ') AS TrimmedString; |
UCASE(s) | 将字符串转换为大写 | 将字符串 runoob 转换为大写:SELECT UCASE("runoob"); -- RUNOOB |
UPPER(s) | 将字符串转换为大写 | 将字符串 runoob 转换为大写:SELECT UPPER("runoob"); -- RUNOOB |
函数名 | 描述 | 实例 |
---|---|---|
ABS(x) | 返回 x 的绝对值 | 返回 -1 的绝对值:SELECT ABS(-1) -- 返回1 |
ACOS(x) | 求 x 的反余弦值(单位为弧度),x 为一个数值 | SELECT ACOS(0.25); |
ASIN(x) | 求反正弦值(单位为弧度),x 为一个数值 | SELECT ASIN(0.25); |
ATAN(x) | 求反正切值(单位为弧度),x 为一个数值 | SELECT ATAN(2.5); |
ATAN2(n, m) | 求反正切值(单位为弧度) | SELECT ATAN2(-0.8, 2); |
AVG(expression) | 返回一个表达式的平均值,expression 是一个字段 | 返回 Products 表中Price 字段的平均值:SELECT AVG(Price) AS AveragePrice FROM Products; |
CEIL(x) | 返回大于或等于 x 的最小整数 | SELECT CEIL(1.5) -- 返回2 |
CEILING(x) | 返回大于或等于 x 的最小整数 | SELECT CEILING(1.5); -- 返回2 |
COS(x) | 求余弦值(参数是弧度) | SELECT COS(2); |
COT(x) | 求余切值(参数是弧度) | SELECT COT(6); |
COUNT(expression) | 返回查询的记录总数,expression 参数是一个字段或者 * 号 | 返回 Products 表中 products 字段总共有多少条记录:SELECT COUNT(ProductID) AS NumberOfProducts FROM Products; |
DEGREES(x) | 将弧度转换为角度 | SELECT DEGREES(3.1415926535898) -- 180 |
n DIV m | 整除,n 为被除数,m 为除数 | 计算 10 除于 5:SELECT 10 DIV 5; -- 2 |
EXP(x) | 返回 e 的 x 次方 | 计算 e 的三次方:SELECT EXP(3) -- 20.085536923188 |
FLOOR(x) | 返回小于或等于 x 的最大整数 | 小于或等于 1.5 的整数:SELECT FLOOR(1.5) -- 返回1 |
GREATEST(expr1, expr2, expr3, …) | 返回列表中的最大值 | 返回以下数字列表中的最大值:SELECT GREATEST(3, 12, 34, 8, 25); -- 34 返回以下字符串列表中的最大值:SELECT GREATEST("Google", "Runoob", "Apple"); -- Runoob |
LEAST(expr1, expr2, expr3, …) | 返回列表中的最小值 | 返回以下数字列表中的最小值:SELECT LEAST(3, 12, 34, 8, 25); -- 3 返回以下字符串列表中的最小值:SELECT LEAST("Google", "Runoob", "Apple"); -- Apple |
LN | 返回数字的自然对数,以 e 为底。 | 返回 2 的自然对数:SELECT LN(2); -- 0.6931471805599453 |
LOG(x) 或 LOG(base, x) | 返回自然对数(以 e 为底的对数),如果带有 base 参数,则 base 为指定带底数。 | SELECT LOG(20.085536923188) -- 3 SELECT LOG(2, 4); -- 2 |
LOG10(x) | 返回以 10 为底的对数 | SELECT LOG10(100) -- 2 |
LOG2(x) | 返回以 2 为底的对数 | 返回以 2 为底 6 的对数:SELECT LOG2(6); -- 2.584962500721156 |
MAX(expression) | 返回字段 expression 中的最大值 | 返回数据表 Products 中字段 Price 的最大值:SELECT MAX(Price) AS LargestPrice FROM Products; |
MIN(expression) | 返回字段 expression 中的最小值 | 返回数据表 Products 中字段 Price 的最小值:SELECT MIN(Price) AS MinPrice FROM Products; |
MOD(x,y) | 返回 x 除以 y 以后的余数 | 5 除于 2 的余数:SELECT MOD(5,2) -- 1 |
PI() | 返回圆周率(3.141593) | SELECT PI() --3.141593 |
POW(x,y) | 返回 x 的 y 次方 | 2 的 3 次方:SELECT POW(2,3) -- 8 |
POWER(x,y) | 返回 x 的 y 次方 |