主键:用于唯一标识表中每个行的一个或多个列
外键:一个或多个用于识别其他表中某一行的列
mysql -u root -p /*用root登录*/
create database bank;
grant all on bank.* to 'lrngsql'@'localhost' identified by 'xyz';
quit;
mysql -u lrngsql -p;
use bank; /*关联数据库*/
使用mysql命令行工具,同时指定用户名和要使用的数据库
mysql -u lrngsql -p bank
定长 char(20)
变长 varchar(20)
指定字符集 Varchar(20) character set gb2312
指定整个数据库的默认字符集create database foreign_sales character set utf8
文本数据tinytext, text, mediumtext, longtext
整数类型tinyint, smallint, mediumint, int, bigint
浮点类型float(p,s),double(p,s)
p数字总位数,s小数点后数字位数
时间类型date, datetime, timestamp, year, time
考虑数据类型,没有重复。
选择主键
子表及其自己的主键,确定指向主表的外键
CREATE TABLE person
(person_id SMALLINT unsigned,
fname VARCHAR(20),
lname varchar(20),
gender CHAR(1), /*可以把检查约束和数据类型定义在一起,
gender ENUM('M','F')*/
birth_date DATE,
street VARCHAR(20),
city varchar(20),
state VARCHAR(20),
country varchar(20),
postal_code varchar(20),
CONSTRAINT pk_person PRIMARY KEY (person_id)
); /*主键约束,创建在person_id上,且被命名为pk_person*/
desc person; /*检查定义*/
create table favorite_food
(person_id SMALLINT unsigned,
food varchar(20),
constraint pk_favorite_food primary key (person_id, food),
constraint fk_fav_food_person_id foreign key (person_id)
references person (person_id)
);/*外键约束,person_id列只能来自perosn表*/
为主键打开自增特性
SET FOREIGN_KEY_CHECKS = 0;
ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED auto_increment;
SET FOREIGN_KEY_CHECKS = 1;
使用INSERT插入:
三部分 表名,列名,列值
INSERT INTO person
(person_id, fname, lname, gender, birth_date)
values (null, 'William', 'Turner', 'M', '1972-05-27');
使用SELECT 和 WHERE查找
三部分:select列,from表,where条件
SELECT person_id , fname, lname, birth_date
FROM person
where person_id = 1;
SELECT person_id, fname, lname, birth_date
from person
where lname = 'Turner';
使用update更新数据
三部分:update表,set 列=值,where条件
update person
set street = '1225 tremont st.',
city = 'Boston',
state = 'MA',
country = 'USA',
postal_code = '01238'
where person_id = 1;
使用delete删除数据
delete from表,where条件
Delete from person
where person_id = 2;
drop table person;
###查看可用的表
show tables;
插入的主键位置已经有了,会出现错误。不如NULL,发挥自增性质
reference person (person_id)约束了person_id列的值必须来自person表
update person
/*set birth_date = 'DEC-21-1980'不合法*/
set birth_date = str_to_date ('DEC-21-1980', '%b-%d-%Y')
/*str_to_date是字符串转为日期的函数*/
where person_id = 1;
select子句用于在所有可能的列中,选择查询结果集要包含哪些列
查询全部列
select *
from department;
select子句可以带上字符、表达式、内建函数、用户自定义函数
select emp_id,
'active' as status,
emp_id * 3.14 as empid_x_pi,
upper(lname) last_name_upper
from employee;
select version(),
user(),
database();
列的别名,as也可以省略
selece emp_id*3.14 as empid_x_pi
去除重复的行
select distinct cust_id
from account;
三种类型的表
- 永久表。create table语句创建的表
- 临时表。子查询select返回的表
- 虚拟表。使用create view子句创建的视图
子查询产生的表
select e.emp_id, e.fname
from (select emp_id, fname, lname, title
from employee) e;
视图,视图被创建后,没有产生或储存任何数据。服务器只是保留该查询以供使用
create view employee_vw AS
select emp_id, fname, lname, year(start_date) start_year
from employee;
select emp_id, start_year
from employee_vw;
表连接,定义表别名
select e.emp_id, e.fname, e.lname, d.name dept_name
from employee as e inner join department as d
on e.dept_id = d.dept_id;
where子句,and和or
select account_id, cust_id, status, avail_balance
from account
where (status = 'ACTIVE') and (avail_balance > 2500);
group by 和having子句
根据列值对数据进行分组。having子句搭配group by子句,功能类似where对分组数据进行过滤
select d.name, count(e.emp_id) as num_employees
from department d inner join employee as e
on d.dept_id = e.dept_id
group by d.name
having count(e.emp_id)>2;
oder by子句,升序或降序
根据表达式排序。right提取右数三个字符
order by right(fed_id,3) desc;
where 语句限制了所选择的行数
AND OR NOT
表达式可以由下面类型中的任意一个:
- 数字
- 表或视图中的列
- 字符串
- 内建函数
- 子查询
- 表达式列表
- 比较算符= != < > <> like in between
- 算术操作符 + - * /
相等条件
where product_type_cd = 'ACCOUNT'
不等条件 <>或!=
使用相等条件修改数据
set sql_safe_updates = 0;
delete from account
where status = 'CLOSED' and YEAR(close_date) = 2002;
范围条件 值是否处于某个区间 < > between
where start_date < '2007-01-01';
where start_date between '2005-01-01' and '2007-01-01';/*必须左小右大*/
成员条件 IN 以及NOT IN
where product_cd in (select product_cd from product
where product_type_cd = 'ACCOUNT');
匹配条件
- 使用通配符
_正好1个字符
%任意数目的字符
select lname
from employee
/*where fed_id like '___-__-____';*/
where lname like '%ba%';
- 正则表达式
只能IS NULL 不能 = NULL
IS NOT NULL
注意确定哪些列可以允许NULL值 一些情况如允许null值,通过!= 比较算符会导致null值被忽略
select e.emp_id, e.fname, e.lname, b.name
from employee e inner join branch b
on e.assigned_branch_id = b.branch_id;
on的作用机理:e中的值1去找b中branch_id列为1的行,获取该行的name 值
顺序交换,得到的结果是一致的
select c.cust_type_cd, c.fed_id, p.name
from customer c inner join account a
on c.cust_id = a.cust_id
inner join product p
on a.product_cd = p.product_cd
where c.cust_type_cd = 'B';
连续两次使用同一个表。选取出两个列,一个是开户行,一个是开户柜员所属行。 branch是行代号和行名称的表,被用到两次。需要两个branch表的实例别名取不同,方便区分。
select a.account_id, e.emp_id, b_a.name as open_branch, b_a.name emp_branch
from account a inner join branch b_a
on a.open_branch_id = b_a.branch_id
inner join employee e
on a.open_emp_id = e.emp_id
inner join branch b_e
on e.assigned_branch_id = b_e.branch_id
where a.product_cd = 'CHK'
order by emp_id;
自连接。因为表中包含了一个指向自身的外键,即指向本表主键的列。如列出雇员主管的名称。
select e.fname, e.lname, e_mgr.fname as mgr_fname, e_mgr.lname as mgr_lname
from employee e inner join employee e_mgr
on e.superior_emp_id = e_mgr.emp_id;
不与自身相同。如柜员之间安排下棋,不能与自己下棋,且每两个人不能重复匹配
select e1.fname, e1.lname, 'VS' vs, e2.fname, e2.lname
from employee e1 inner join employee e2
on e1.emp_id < e2.emp_id
where e1.title = 'Teller' and e2.title = 'Teller';
- union并集
- intersect交集
- except(A except B 集合A减去集合B的结果)
两个数据集执行集合操作时,必须满足两个规范:
- 两个数据集有同样数目的列
- 两个数据集中对应列的数据类型必须是一样的
select语句中可以使用集合操作符执行集合操作,每个select查询语句都会产生一个包含单个行的数据集合
union和union all操作符
union删除重复项
select 'ind' type_cd, cust_id, lname
from individual
union all
select 'bus' type_cd, cust_id, name
from business
union
select 'bus' type_cd, cust_id, name
from business
order by lname;
intersect和except操作符MySQL暂不支持
order by指定要排序的列时需要从复和查询(select)的第一个查询中选择列名
字符数据类型
- char 固定长度
- varchar 边长字符串
- text 容纳大长度的变长字符串
create table string_tbl
(char_fld char(30),
vchar_fld varchar(30),
text_fld text);
insert into string_tbl (char_fld, vchar_fld, text_fld)
values ('this is char data',
'this is varchar data',
'this is text data');
update string_tbl
set vchar_fld = 'this is a piece of extremely long varchar data';
包含单引号 需要在单引号前加'作为转义符
update string_tbl
set text_fld = 'this string didn\'t work. but it does now.';
quote() 使用单引号将字符串包含起来,并为字符串中单引号添加转义符
concat() 连接
特殊符号
select concat('danke sch', char(148), 'n');
length() 字返回字符串长度
position() 返回子字符串位置
locate() 返回字符串位置,可以指定搜索的起点
注意第一个字符位置号为1
select position('is' in vchar_fld)
from string_tbl;
select locate('is',vchar_fld, 4)
from string_tbl;
Strcmp() 比较字符串前后位置。-1表示第一个字符串在第二个字符串之前,0表示两个字符串相同,1表示第一个字符串在第二个字符串之后
insert into string_tbl (vchar_fld) values('abcd');
insert into string_tbl (vchar_fld) values('xyz');
select strcmp('xyz', 'abcd') xyz_abcd;
like 操作符比较是否出现特定字符串
select name, name like '%ns' ends_in_ns
from department;
正则表达式
concat()
select concat(fname,' ', lname, 'has been a ', title, 'since ', start_date) emp_narrative
from employee
where title = 'Teller' or title = 'Head Teller';
insert() 在字符串中间增加或替换部分字符。insert接收四个参数,原始字符串,字符串操作开始的位置,需要替换的字符串,替换入的字符串。如果第三个参数为0,表示向右排放
update string_tbl
set vchar_fld = insert(vchar_fld, 1, 0,'insert sth');
substring() 提取字符串
select substring(vchar_fld, 2,3) /*第2个位置提取3个字符*/
from string_tbl;
+、-、*、/
执行算术运算,各种Acos(), Asin(), Atan()函数
mod() 求余数
select mod(10,2);
pow() 求幂
select pow(2,3) '2^8';
ceil向上取整, floor向下取整
select ceil(72.44445),floor(72.2131);
round()
- 四舍五入
- 四舍五入保留指定位数。负数取整
select round(72.3333,2);
truncate() 小数点后需要被截取多少位。若为负数,表示取整多少位-1代表10
sign()返回1代表正数
abs()
时区
- 从已有的date、datetime或time列中复制数据
- 执行返回date、datetime或time型数据的内建函数
- 构建可以被服务器识别的代表日期的字符串
- DATE YYYY-MM-DD
- DATETIME YYYY-MM-DD HH:MI:SS
- TIMESTAMP YYYY-MM-DD HH:MI:SS
- TIME HHH:MI:SS
服务器可以接受datetime类型字符串,将对其自动转换
update transaction
set txn_date = '2008-09-17 15:30:30'
where txn_id = 1;
还有一种手动转换为datetime的方法,cast()
select cast('2008-09-01' as date) date_field;
根据字符串产生时间数据,但不是标准格式cast接受不了,使用内建函数str_to_date()转化为日期字符串。各种符号意义见书
select str_to_date('01 17, 2008','%m %d, %Y') set_new_date;
当前日期 current_date() current_time() current_timestamp()
加上一段时间date_add(current_date(),interval 5 day)
当月最后一天last_dat()
转换时区convert_tz()
提取日期信息select extract(year from '2008-01-01');
确定星期几dayname('2008-01-01');
时间间隔 select datediff('2018-09-01','2018-01-01');
cast() 将字符串转换为指定数据类型
select cast('-111' as signed integer);
- max()
- min
- avg
- sum
- count
select product_cd,
max(avail_balance) max_balance,
min(avail_balance) min_balance,
avg(avail_balance) avg_balance,
sum(avail_balance) tot_balance,
count(*) numaccounts
from account
group by product_cd;
select count(distinct open_emp_id)
from account;
select max(pending_balance - avail_balance) max_uncleared
from account;
count(*)对所有行计数
count(列名)忽略null值
select product_cd, sum(avail_balance) prod_balance
from account
group by product_cd;
select product_cd, open_branch_id,
sum(avail_balance) prod_balance
from account
group by product_cd, open_branch_id
order by product_cd;
select extract(year from start_date) year,
count(*) how_many
from employee
group by extract(year from start_date);
分别求和再对product_cd单独求和,求出所有行总和
select product_cd, open_branch_id,
sum(avail_balance) tot_balance
from account
group by product_cd, open_branch_id with rollup;
where应该在分组之前执行,having过滤条件在分组之后搭配使用
select open_emp_id, count(*) how_many
from account
where status = 'ACTIVE'
group by open_emp_id
having count(*) > 4;
总结:
- 它返回的结果可以是单列/单行,单列/多行,多列/多行,如果返回结果多于1行,他可以用于比较,而不能用于等式判断
- 它可以独立于包含语句(非关联子查询)
- 它可以引用包含语句中一行或多行(关联子查询)
- 它可以用于where条件中,这些where条件使用比较算符以及其他特殊目的的算符(in, not in, exists, not exists);
- 它可以出现于select, update, delete, insert
- 它产生的结果集可以与其他表或者子查询连接
- 它可以生成值用来insert填充表或者一些列
- 可以用于select、from、where、having、order by
单行单列子查询结果,成为标量子查询。可以位于= <> < > <= >= 任意一边
select account_id
from account
where open_emp_id <> (select e.emp_id
from employee e inner join branch b
on e.assigned_branch_id = b.branch_id
where e.title = 'Head Teller' and b.city = 'Woburn');
select emp_id
from employee
where emp_id not in (null);
使用not in或<> 比较一个值和一个值集的,读者必须注意确保值集中不含null值。否则会出现错。null与is not搭配
与<> < > =搭配使用
只要有一个比较成立,则条件为真
select account_id
from account
where open_branch_id = (select branch_id
from branch
where name = 'Woburn Branch')
and open_emp_id in (select emp_id
from employee
where title = 'Teller' or title = 'Head Teller');
/*使用了两个单列子查询*/
先执行包含查询,再执行子查询
select c.cust_id
from customer c
where 2 = (select count(*)
from account a
where a.cust_id = c.cust_id);
/*先从customer表中检索出13行,接着为每一行执行子查询,
每次执行都要包含查询向子查询传递传递客户ID
若子查询返回值2,则过滤调价满足,该行才被添加到结果集*/
若只关心关系而不在于数量。查询是否至少返回1行
select a.account_id, a.product_cd, a.cust_id, a.avail_balance
from account a
where exists (select 1
from transaction t
where t.account_id = a.account_id
and t.txn_date = '2003-07-30');
set sql_safe_updates = 0;
update account a
set a.last_activity_date =
(select max(t.txn_date)
from transaction t
where t.account_id = a.account_id)
where exists (select 1
from transaction t
where t.account_id = a.account_id);
/*exist 查询每个账户是否发生过交易*/
子查询在from子句中使用必须是非关联的,它首先执行然后一直保留于内存中直至包含查询执行完毕。
select d.dept_id
from department d inner join
(select dept_id, count(*) how_many
from employee
group by dept_id) e_cnt
on d.dept_id = e_cnt.dept_id;
如对客户按照账户里余额多少进行分组
以下product、branch、employee这三个表只是用于描述。account表已有分组所需的信息(product_cd, open_branch_id, open_emp_id, avail_balance) 将分组任务独立出来,然后将子查询生成的三个表连接成一个表,最后得到最终结果。
select p.name product, b.name branch,
concat(e.fname, ' ', e.lname) name,
account_groups.tot_deposits
from
(select product_cd, open_branch_id branch_id,
open_emp_id emp_id,
sum(avail_balance) tot_deposits
from account
group by product_cd, open_branch_id, open_emp_id) account_groups
inner join employee e on e.emp_id = account_groups.emp_id
inner join branch b on b.branch_id = account_groups.branch_id
inner join product p on p.product_cd = account_groups.product_cd
where p.product_type_cd = 'ACCOUNT';
在having中使用子查询来查找开户最多的雇员
用于select, order by, insert中的values语句
left外连接包括第一个表的所有行,但仅仅包含第二个表中那些匹配行的数据。right外连接则相反
select a.account_id, a.cust_id, b.name
from account a left outer join business b
on a.cust_id = b.cust_id;
select a.account_id, a.product_cd,
concat(i.fname, ' ', i.lname) person_name,
b.name business_name
from account a left outer join individual i
on a.cust_id = i.cust_id
left outer join business b
on a.cust_id = b.cust_id;
将employee表连接到自己而生成雇员和他们主管的列表
select e.fname, e.lname,
e_mgr.fname mgr_fname, e_mgr.lname mgr_lname
from employee e left outer join employee e_mgr
on e.superior_emp_id = e_mgr.emp_id; /*!!!*/
笛卡尔积连接
select days.dt, count(t.txn_date) /*与group by搭配使用*/
from transaction t right outer join
(select date_add('2008-01-01',
interval (ones.num + tens.num + hundreds.num) day) dt
from
(select 0 num union all
select 1 num union all
select 2 num union all
select 3 num union all
select 4 num union all
select 5 num union all
select 6 num union all
select 7 num union all
select 8 num union all
select 9 num) ones
cross join
(select 0 num union all
select 10 num union all
select 20 num union all
select 30 num union all
select 40 num union all
select 50 num union all
select 60 num union all
select 70 num union all
select 80 num union all
select 90 num) tens
cross join
(select 0 num union all
select 100 num union all
select 200 num union all
select 300 num) hundreds
where date_add('2008-01-01', interval (ones.num + tens.num + hundreds.num) day) <
'2009-01-01') days
on days.dt = t.txn_date
group by days.dt
order by 1; /*按第一列排序*/
自然连接 是指依赖多表交叉时的相同列名来自动推断正确的连接条件。如果没有相同的列名,不要这么做会出现错误
select a.account_id, a.cust_id
from account a natural join customer c;
select c.cust_id, c.fed_id,
case
when c.cust_type_cd = 'I'
then concat(i.fname, ' ', i.lname)
when c.cust_type_cd = 'B'
then b.name
else 'Unknow'
end name
from customer c left outer join individual i
on c.cust_id = i.cust_id
left outer join business b
on c.cust_id = b.cust_id;
语法如下
case (c0)
when c1 then e1
when c2 then e2
(else ed)
end
语法如下,V0代表一个值,符号V1,V2,...VN代表要与V0比较的值
case V0
when V1 then e1
when V2 then e2
(else ed)
end
count计算一个值再进行比较
select c.cust_id, c.fed_id, c.cust_type_cd,
case (select count(*) from account a
where a.cust_id = c.cust_id)
when 0 then 'None'
when 1 then '1'
when 2 then '2'
else '3+'
end num_accounts
from customer c;
与exist搭配使用
select c.cust_id, c.fed_id, c.cust_type_cd,
case
when exists (select 1 from account a
where a.cust_id = c.cust_id
and a.product_cd = 'CHK') then 'Y'
else 'N'
end has_checking,
case
when exists (select 1 from account a
where a.cust_id = c.cust_id
and a.product_cd = 'SAV') then 'Y'
else 'N'
end has_savings
from customer c;
select a.cust_id, a.product_cd, a.avail_balance/
case
when prod_tots.tot_balance = 0 then 1
else prod_tots.tot_balance
end percent_of_total
from account a inner join
(select a.product_cd, sum(a.avail_balance) tot_balance
from account a
group by a.product_cd) prod_tots
on a.product_cd = prod_tots.product_cd;
select <some calculation>
case
when avail_balance is null then 0
else avail_balance
end