· 数据库的概念
1)结构化查询语言(Structured Query Language)简称SQL;
2)数据库管理系统(Database Management System)简称DBMS;
3)数据库管理员(Database Administration)简称DBA,功能是确保DBMS的正常高效运行;
· SQL常用的3个部分
1)数据查询语言(DQL):其语句也称“数据库检索语句”,用以从表中获得数据,保留字SELECT经常使用,DQL也是所有SQL中用的最多的,其他保留字还有WHERE, ORDER BY, GROUP BY和HAVING这些保留字还与DML一起使用;
2)数据操作语言(DML):其余局包括动词INSERT,UPDATE和DELETE。他们分别用于添加,修改和删除表中的行。也称动作语言;
3)数据定义语言(DDL):DDL主要用于操作数据库。
2. SQL列的常用类型
MySQL: | Java:
INT | int
BIGINT | long
DECIMAL | BigDecimal
DATE/DATETIME | java.util.Date
VARCHAR | String
mysql -u root -p admin;
show databases
· create database 数据库名称;
drop database 数据库名称;;
# 修改数据库编码格式
alter database 数据库名称 charset=编码格式;
show tables;
· 表的约束
1)非空约束:NOT NULL,不允许某列的内容为空;
2)设置列的默认值:DEFAULT;
3)唯一约束:UNIQUE,该表中,该列的内容必须唯一;
4)主键约束:PRIMARY KEY,非空且唯一;
5)主键自增长:AUTO_INCREMENT,从1开始,步长为1;
6)外键约束:FOREIGN KEY,A表中的外键列。A表中的外键列的值必须参照于B表中的某一列(B表主键)。
· 建表
1)建表语法:
CREATE TABLE 表名(
字段1 字段1的类型 [约束], //注意这里有逗号
字段2 字段2的类型 [约束],
....
字段N 字段N的类型 [约束]
//字段最后设置外键,如果有多个外键,外键之间无逗号
// 注意:最后一行没有逗号
)
(2)主键语法:
·单个字段做主键:可以直接在字段定义后写上主键约束:
cno VARCHAR ( 10 ) PRIMARY KEY NOT NULL,-- 课程编号
·多个字段组合作为主键,在字段定义结束后写主键约束:
PRIMARY KEY ( sno, cno, year_semester )
//以(sno, cno, year_semester)的组合作为主键
(3)外键语法:
CONSTRAINT 外键名
FOREIGN key(当前表中的字段) REFERENCES 该外键做主键的表(该外键在做主键的表中的字段名)
举例:(在当前表中定义三个外键)
CONSTRAINT for_spno1 FOREIGN KEY ( spno ) REFERENCES speciality19 ( spno ),
CONSTRAINT for_ctno1 FOREIGN KEY ( ctno ) REFERENCES cousetype19 ( ctno ),
CONSTRAINT for_dno1 FOREIGN KEY ( dno ) REFERENCES department119 ( dno )
(4)综上,建表示例:
DROP TABLE IF EXISTS 't_student';
// 如果表存在就先移除,因为不能存在两个一样名称的表
CREATE TABLE course19 (
cno VARCHAR ( 10 ) PRIMARY KEY NOT NULL,-- 课程编号
cname VARCHAR ( 20 ) NOT NULL,-- 课程名称
spno CHAR ( 2 ),
ctno CHAR ( 1 ),
experiment SMALLINT,-- 实验时数
hours SMALLINT,-- 授课学时
semester SMALLINT,-- 开课学期
credit SMALLINT,-- 课程学分
duty_tno CHAR ( 7 ),
dno CHAR ( 2 ),-- 课程归属院系代码(外键)
CONSTRAINT for_spno1 FOREIGN KEY ( spno ) REFERENCES speciality19 ( spno ),
CONSTRAINT for_ctno1 FOREIGN KEY ( ctno ) REFERENCES cousetype19 ( ctno ),
CONSTRAINT for_dno1 FOREIGN KEY ( dno ) REFERENCES department119 ( dno )
);
注意:建表不要使用关键字
·删除表
(1)语法:
DROP TABLE 表名;
· 语法
UPDATE 表名
SET 列1 = 值1, 列2 = 值2, ...
WHERE [条件]
· 举例:
UPDATE student119 set sname='龙傲天' WHERE sname='小龙';
//将小龙的姓名改为龙傲天
注意:不要省略where条件子句,省略的话,全表数据都会被修改。
INSERT INTO 表名(字段1,字段2....)VALUES(值1,值2.....);
// 1、插入完整数据记录
INSERT INTO t_student (name, email, age) VALUE ("xiaoming", "[email protected]", 16);
// 2、插入部分数据记录
INSERT INTO t_student (name, age) VALUE ("xiaoming", 16);
// 3、插入查询结果
INSERT INTO t_student(name, email, age) SELECT name,email,age FROM t_student;
· 语法
DELETE FROM 表名 条件
· 举例:
DELETE FROM student119 WHERE sid=202026010111;
注意:Where子句别省略,否则全表的数据都会被删除。
SELECT DISTINCT 列名, ... FROM 表名;
· 实战:
SELECT DISTINCT productName, brand FROM product;
· 算术运算符的使用范围
1)对 number 型数据可以使用算术运算符(+,-,*,/)对数据进行操作;对date型数据可以使用部分算术运算符(+,-)对数据进行操作。
· 算术运算符的优先级
1)与数学中运算相同
·举例
// 查询所有货物的id,名称和批发价(折扣价=销售价*折扣)
SELECT id, productName, salePrice * disCount From product;
· 作用
1)改变列的标题头;
2)作为计算结果的含义;
3)作为列的别名;
4)如果别名使用特殊字符(强烈不建议使用特殊字符),或是强制大小写或有空格时都需要加单引号。
· 语法
// 第一种
SELECT 列名 AS 别名 FROM 表名 [WHERE];
// 第二种
SELECT 列名 别名 FROM 表名 [WHERE];
· 举例
// 查询所有货物的id,名称和折扣价价(折扣价=销售价*折扣)(使用别名)
SELECT id, productName, salePrice * disCount AS price From product;
1)为了方便用户浏览查询结果数据,有时需要设置查询结果的显示格式,可以使用CONCAT函数来连接字符串。
· 语法
CONCAT(字符串1, 字符串2, ...)
· 举例
// 查询商品的名字和零售价。格式:xxx商品的零售价为:ooo
SELECT CONCAT(productName, " 商品的零售价为:", salePrice) FROM product;
1)不等于:<> 等价 !=;
· 举例
// 查询商品名为 罗技G9X 的货品信息
SELECT * FROM product WHERE productName = "罗技G9X";
// 查询批发价大于350的货品信息(折扣价 = 销售价*折扣)
SELECT *, salePrice * discount FROM product WHERE salePrice * discount > 350;
1)AND:如果组合的田间都是 true,返回true;
2)OR:如果组合的条件之一是true,返回true;
3)NOT:如果给出的条件是false,返回true;如果给出的条件是true,则返回false。
· 举例
// 查询售价在300-400(包括300和400)的货品信息
SELECT * FROM product WHERE salePrice >= 300 ADN salePrice <= 400;
// 查询分类编号为2, 4的所有货品信息
SELECT * FROM product WHERE dir_id = 2 OR dir_id = 4;
// 查询编号不为2的所有商品信息
SELECT * FROM product WHERE NOT dir_id = 2
1)范围匹配:BETWEEN AND 运算符,一般使用在数字类型的范围上。但对于字符数据和日期类型同样可用。
注意:BETWEE AND 使用的是闭区间。
· 语法
// 使用的是闭区间,也就是包括minValue 和 maxValue
WHERE 列名 BETWEEN minValue AND maxValue;
· 举例
// 查询零售价不在 300 - 400 之间的货品信息
SELECT * FROM product WHERE NOT salePrice BETWEEN 300 AND 400;
2)集合查询:使用 IN 运算符,判断字段的值是否在指定的集合中。
· 语法
WHERE 列名 IN (值1, 值2, ...);
· 举例
// 查询分类编号为 2,4 的所有货品的 id,货品名称
SELECT id, productName FROM product WHERE dir_id IN (2,4);
// 查询分类编号不为 2, 4 的所有货品的 id,货品名称
SELECT id, dir_id, productName FROM product WHERE NOT dir_id IN (2,4);
1)IS NULL:判断列的值是否为空值,非空字符串,空字符串使用 == 判断;
· 语法
WHERE 字段N IS NULL;
· 实战
// 查询商品名为NULL的所有商品信息
SELECT * FROM product WHERE productName IS NULL;
SELECT * FROM product WHERE supplier = "";
结论:使用 = 来判断只能判断空字符串,不能判断 null;而使用 IS NULL 只能判断 null 值,不能判断空字符串。
1)LIKE:模糊查询数据使用 LIKE 运算执行通配符;
2)通配符:% 表示可有零个或多个任意字符; _ 便是需要一个任意字符;
· 语法
WHERE 列名 LIKE "%M_";
· 举例
// 查询货品名称以 罗技M9* 结尾的所有货品信息,这里的 * 表示一个任意字符,它不具备任何意义,只是我出于题目需要才这样写,便于你理解而已
SELECT * FROM product WHERE productName LIKE "%罗技M9_";
1)ORDER BY:使用 ORDER BY子句将查询结果进行排序,ORDER BY子句出现在 SELECT 语句的最后;
2)ASC:升序;DESC:降序;
· 实战
// 查询 id,货品名称,分类编号,零售价 按分类编号降序排序,如果分类编号相同再按零售价升序排序
SELECT * FROM product ORDER BY dir_id DESC, salePrice ASC;
1)Group By:分组查询,一般与having一起用,查询的参数只能是统计函数和分组的条件参数,或者是聚合函数,having 后面的条件只能是分组查询的字段或者统计函数;
· 实战
# 查询分组条件之外的值,拼接成字符串
select theme_id, group_concat(dest_id) dest_id, group_concat(dest_name) dest_name
from strategy group by theme_id having theme_id = 1;
1)FROM字句:从哪张表中去查数据;
2)JOIN table:先确定表,在确定关联条件;
3)ON 条件:表绑定条件;
4)WHERE字句:筛选需要的行数据;
5)GROUP BY子句:分组操作;
6)HAVING:对分组后的记录进行聚合;
7)SELECT字句:筛选需要显示的列数据;(对字段 AS 起别名是在这时候,所以 WHERE 中不能用字段的别名,ORDER BY 中可以使用别名);
8)DISTINCT:去重;
9)ORDER BY子句:排序操作;
10)LIMIT子句:限制条件;
· 概念
1)COUNT(*):统计表中有多少条数据;
2)SUM(列):汇总列的总和;
3)MAX(列):获取某一列的最大值;
4)MIN(列):获取某一列的最小值;
5)AVG(列):获取某一列的平均值;
· 举例
// 查询货品表共有多少数据
SELECT COUNT(*) FROM product;
// 计算所有货品的销售价
SELECT SUM(costPrice) FROM product;
7.1 引入一个题目分析,一个工程公司的数据库有如下四张表:
1) 员工表Employee(emp_id,name,sex, birth_day,title,phone, salary,dept_id),其属性分别表示员工工号、姓名、性别,出生日期、岗位,联系电话,工资,所属部门的编号;
2)部门表Department(dept_id,name,phone,manager_id), 其属性分别表示部门编号、部门名称,部门电话,部门经理的员工工号;
3)工程项目表project(proj_id,name,location,budget, dept_id), 其属性分别表示项目编号、项目名称,项目地点,项目金额,项目承担部门的编号。假定一个项目只由一个部门承担。一个部门可承担多个项目;
4)职工参加工程项目年度情况表Work(emp_id, proj_id,year,start_date, end_date, duty,workdays), 其属性分别表示员工工号,项目编号,年度,开始日期,结束日期,员工在项目中的职责,员工实际参与项目的天数。
注意:Work表记录了每个员工的年度工作情况。一个项目可能要多年完成,因此一个员工可能在多年中都参加了同一个项目,此时,每年都会有一行记录。在一年中,一个员工也可参加多个项目。duty取值有PROJ_MANAGER,GROUP_LEADER, MEMBER三种。对于一个项目,除了其承担部门的员工可参加外,其它部门的员工也可能参加。
7.2 回答以下问题:
一、分别表示4个表的主键,如果有外键也请标识出来:
注意点:指明外键时,要指明该外键引用了哪个表的主键!!!!
回答:
· Employee 主键为:emp_id
外键为:dept_id 引用了Department的主键。
·Department主键为:dept_id
外键为:manager_id 引用了Employee的主键。
· project主键为: proj_id
外键为:dept_id 引用了Department的主键。
· Work主键为:(emp_id,proj_id,year)
外键1:emp_id 引用了Employee的主键;
外建2:proj_id 引用了project表的主键。
二、用关系代数式表达:
(1)查找出(名称为‘工程1队’)的部门的女职工名单,输出姓名、出生日期、工资。
(2) 求出在2017年根本就没有参加任何项目的员工名单,输出所在部门名称、姓名、员工工号。
三、使用SQL的DDL写出Work表的定义,要定义主键、外键等数据库的完整性约束
注意 1:创建表时,要根据题目中的要求,注意字段的域约束!!!!(包括非空、字段类型、字段范围)
注意 2 :注意外键约束、主键、组合主键的正确写法!
CREATE TABLE Work(
emp_id CHAR(5) NOT NULL,//非空记得设置
proj_id CHAR(5) NOT NULL,
year CHAR(4) NOT NULL,
start_date DATE,
end_date DATE,
duty VARCHAR(20) (CHECK duty IN(‘PROJ_MANAGER’,‘GROUP_LEADER’,‘MEMBER’)),//注意这里!
workdays INTEGER
PRIMARY KEY(emp_id, proj_id, year)
FOREIGN KEY(emp_id) REFERENCES employee(emp_id)
Foreign KEY(proj_id) REFERENCES project(proj_id)
//注意!如果有多个外键,外键定义之间没有逗号
);
四、使用SQL语句回答下列十四个问题:
(1)新进一个工程项目:名称:望城电力大楼, 地点:雷锋大道428号,项目金额850万元, 由工程5队(部门编号为‘07’)来负责承担,公司给该工程分配编号‘2018101’。试用SQL 语句将该工程项目信息加到数据库中;
INSERT INTO project (proj_id,name,location,budget,dept_id) VALUES('2018101','望城电力大楼','雷锋大道428号',850,'07');
(2)土建部的员工张开意(工号为‘2011045’)已经从公司调走,试用SQL 语句将该员工的信息从数据库中删除;
DELETE FROM employee WHERE emp_id ='2011045';
(3)员工李欣艺(工号:‘2008013’)从工程1队(部门编号‘03’)调任工程2队(部门编号‘04’)任部门经理,试用SQL 语句修改数据库,反映此调动和任命;
UPDATE employee SET dept_id='04' WHERE emp_id='200813';
//先修改个人信息
UPDATE department SET manager_id='200813' WHERE dept_id='04';
//再修改部门经理的信息
(4)求出2017年公司的劳动模范。其定义是:凡是全年参加项目的实际总计天数大于等于300天的员工就是劳动模范。要求输出姓名、工号、所属部门名称,工作天数,要求对输出结果按照工作量递减原则排序;
思路:首先在work表中挑选出满足劳动模范需求的员工,然后结合各表联系,输出对应的数据。
因此有两种解法:
· 创建临时表法:
SELECT emp_id, SUM(workdays) AS workdays INTO tb1 FROM work WHERE year ='2017' GROUP BY emp_id HAVING workdays >=300;
//这里注意因为一个员工可能会参加不同的项目,因此
//要根据emp_id进行分组累加工作天数
SELECT E.name,E.emp_id,D.name,t.workdays FROM tb1 AS t, employee AS e,
department AS d WHERE e.emp_id =w. emp_id AND e. dept_id =d. dept_id
ORDER BY workdays DESC;
//注意DESC是降序,ASC是升序
· 语句嵌套法:
SELECT E.name,E.emp_id,D.name,workdays FROM (SELECT emp_id, SUM(workdays) AS workdays
FROM work WHERE year ='2017' GROUP BY emp_id HAVING workdays >=300) AS w,
employee AS e, department AS d WHERE e. emp_id =w. emp_id AND e. dept_id =d. dept_id
ORDER BY workdays DESC;
(5)求出在2017年全年根本就没有参加任何项目的员工名单,输出所在部门编号,姓名,员工工号和年龄;
注意:巧用NOT IN 、DISTINCT 关键字,并且了解年龄的计算方式。
select e.dept_id,e.name,e.emp_id,(YEAR(NOW())-YEAR(e.birthday)) AS age
FROM employee AS e where e.emp_id
NOT IN (Select DISTINCT emp_id From work AS w WHERE w.year='2017');
(6)求出工资低于职工所在部门平均工资的职工工号和姓名;
注意点:GROUP BY的字段,必须出现在查询的队列中!
· 临时表法:
Select AVG(salary) AS avg_salary,dept_id INTO tb1 FROM
employee AS e GROUP BY dept_id;
-- 注意这里按照dept_id分组的话,前边的SELECT中必须有这个字段
Select e.emp_id,e.name FROM tb1 AS t,employee AS e
WHERE e.salary
· 语句嵌套法:
Select e.emp_id,e.name from
(select Avg(salary) AS avg_salary,dept_id From employee AS e GROUP BY dept_id )
AS s,employee AS e Where e.dept_id=s.dept_id AND e.salary < avg_salary;
(7)求出工资最高的员工的职工工号、姓名和工资;
SELECT emp_id,name,salary FROM employee WHERE salary=
(SELECT MAX(salary) FROM employee);-- 注意这里!!
注意:这里不能直接salary=MAX(salary)!!!
(8)求出参加了所有工程项目的职工工号和姓名;
思路:统计每个员工参加项目的数量,然后统计项目的总数,如果两个数量一致,就满足条件。
Select e.emp_id,e.name FROM employee AS e,(SELECT COUNT(w.proj_id) AS
w_num,w.emp_id FROM work AS w GROUP BY w.emp_id
HAVING w_num=(SELECT COUNT(*) FROM project)) AS s
WHERE s.emp_id=e.emp_id;
(9)创建一个视图dept_info(name, code, manager, clerk_num),其内容为部门概况:部门名称,部门编号,部门经理姓名,部门员工人数);
· 首先复习一下视图的创建语法:
CREATE VIEW 视图名 (字段1、字段2、字段3.....字段n) --视图中显示的字段
AS
BEGIN
~ ~~ ~ -- (SQl语句,选择是由表中的哪些字段构成)
END;
答:
CREATE VIEW dept_info(name, code, manager, clerk_num)
AS
BEGIN
SELECT d.name,dept_id,e.name,s.dept_num FROM
(SELECT dept_id,COUNT(*) AS dept_num From employee GROUP BY
dept_id) AS s,department AS d,employee AS e
WHERE d.dept_id=s.dept_id AND manager_id=e.emp_id;
END;
(10) 创建一个存储过程my_work,列出某个员工,在某年中参加项目的情况,输出包括起始日期,结束日期,项目名称proj_name,项目承担部门的名称dept_name,工作天数。要求按照起始日期升序排列;
首先复习一下存储过程的创建语法:
CREATE PROCEDURE 存储过程名(参数1,参数2.....参数n)
AS
BEGIN
~~ //SQL语句
END;
注意参数的书写格式:
IN +参数名+参数类型
解答:
CREATE PROCEDURE my_work(@ emp_id IN VARCHAR,@ year IN VARCHAR)
AS -- 我在Navicat中写存储过程时不用这个
BEGIN
SELECT start_date,end_date,p.name AS proj_name,d.name
AS dept_name, workdays FROM work AS w, project AS p, department
AS d WHERE w.proj_id=p.proj_id AND p.dept_id=d.dept_id
AND w.emp_id=@emp_id AND w.year=@year ORDER BY start_date ASC;
END;
注意:如果要使用参数的值,就要使用标识符@。
(11) 创建一个触发器add_work_rule,控制往Work表中添加记录时,表中不会出现一个员工在一年中参加多个项目时,其时间上有重叠现象。即在Work表中当出现两行记录的emp_id, year相同时,不允许其(start_date, end_date)时间段上有重叠;
· 首先复习一下触发器的创建语法:
CREATE TRIGGER 触发器名
触发器类型 触发条件 ON 操作的表名
REFERENCING NEW ROW AS new
FOR EACH ROW
BEGIN
~~~ //SQL语句
END;
· 触发类型有:
BEFORE:先执行触发器中的操作,再执行输入的SQL语句
AFTER :先执行输入的SQl语句,再执行触发器中的操作。
INSTEAD OF:不执行输入的SQL语句,只执行触发器中的操作。
· 解答:
CREATE TRIGGER add_work_rule
BEFORE INSERT ON work
REFERENCING NEW ROW AS new
FOR EACH ROW
BEGIN
WHEN(EXIST SELECT * FROM Work WHERE [email protected]_id
AND [email protected]_id AND end_date >[email protected]_date
AND start_date<[email protected]_date)
raise_application_error(20000,'这个记录与已有记录在时间上有重叠');
END;
(12) 创建一个角色group_leader和一个用户ZhangDF, 给group_leader赋予对work表中记录的end_date和workdays进行修改的权限。然后把角色group_leader指派给用户ZhangDF;
注意:
1. 创建角色时,要给角色赋初始密码,语法为:
CREATE USER 角色名 IDENTIFIED BY 密码;
2.赋予某角色(某用户)对于特定表的权限,语法为:
GRANT 权限(字段1,字段2) ON 表名 TO 角色名(用户名);
· 解答:
CREATE ROLE 'group_leader';
CREATE USER ZhangDF IDENTIFIED BY '123';//注意!别忘了给UER设置密码
GRANT UPDATE(end _date,workdays) ON work TO group_leader;
GRANT group_leader To ZhangDF;
(13) 就Work表的(year, proj_id)字段创建一个索引;
· 创建索引的语法:(千万别忘了写索引名!)
CREATE INDEX 索引名 ON 表名(字段1,字段2...);
· 解答:
CREATE INDEX work_year_proj ON work(year,proj_id);