SQL笔记

视频:SQL进阶教程系列为'CodeWithMosh'全套10小时教程

红字部分不保对)

一.基本内容补充
  • MySQL workbench左侧Schemas显示当前所有databases,包含各自的tables,views,stored procedures,functions等。

  • 双击其中一个database 该database加粗 等价于 use 'sql_store'

  • tables中每个table旁边最右边的表格 查看完整表格 等价于 select * from orders

  • tables中每个table旁的扳手符号 查看该表格的列名和属性

PK主键 NN非空值 AI增加记录时自动增一 Default当没有值时的默认值

  • 自己创建的名字可以不用加反引号 (牛客网上的答案也是这样)

二.SQL语法
  • 选出属性,可以进行加减乘除模计算. select points+10 from table1;

  • 对日期做大小比较条件 需要加上引号 >'2000-08-30'

  • and or多个条件 A and (B or C) and的运算优先级高于 or

  • between的使用 where points between 1000 and 3000;表示[1000,3000]

REGEXP运算符 正则表达式:搜索字符串时非常强大

WHERE address LIKE '%trail%';
WHERE address REGEXP 'trail';       #表示包含trail
WHERE address REGEXP '^trail';      #表示必须以trail开头
WHERE address REGEXP 'trail$';      #表示必须以trail结尾
WHERE address REGEXP 'trail|flower' #表示要么包含有trail要么包含flower |前后不能加空格
WHERE address REGEXP '[GIM]e'       #表示包含要么Ge 要么 Ie 要么Me  
WHERE address REGEXP '[a-h]e'       # [ ] - 表示字符范围
#举例  以EN或O结尾
where name REGEXP 'EN$|O$'
  • NULL运算符 获取缺失的数据 is null 或者 is not null进行条件判断

  • ORDER BY select A,B from table order by C,D order by的属性不一定在select之中,也可以排序

order by的属性也可以不是列名,算术表达式 比如 按照 单价*数量 排序

select *, unit_price*quantity as total_price 
from table1
order by total_price   #上面as过了,可以直接写成total_price而不是算式表达式
  • LIMIT 通过添加偏移量,在给数据分页时很有用

limit 6,3 表示第7到第9条数据 6表示偏移量

三.第三章
  • JOIN

(INNER) JOIN取两表交集

SELECT 
order_id,
o.customer_id,  --两个表都有customer_id 需要加前缀 否则报错列不明确
--下面给orders起了别名,其他所有位置都要用别名
full_name
from orders o     --orders customers作为前缀有很多 改个名字 这里as可省略
join customers c
on o.order_id=c.order_id

跨数据库JOIN

--当前数据库是A 想连接数据库B中的product表
SELECT *
from orders o    
join B.product p    --需要给不在当前数据库中的表格前加上 数据库. 作为前缀
on o.order_id=p.order_id

self join 注意每个表都要加别名 不然select列不准确; select相同的属性 最好也要as

自连接无法使用using

SELECT    --找到员工对应的上司 
e.employee_id,
e.first_name,
m.first_name as manager
FROM employees e
JOIN employees m               --join 完了之后select id,员工名,上司名
ON e.reports_to = m.employee_id;

多表连接

select * from A 
join B 
on A.aa=B.bb 
join C 
on A.xx=C.tt;

有复合主键时的JOIN 使用复合连接条件 SELECT * FROM A JOIN B ON (条件1) AND (条件2)

隐式连接语法 (尽量不要使用)

select *
from orders o
join customers c
on o.id = c.id;
--隐式连接语法
select * 
from orders o,customers c
where o.id=c.id;

OUTER JOIN 即LEFT JOIN; RIGHT JOIN 希望将左or右表格中的内容全部输出即使不满足ON的条件

outer join也可以多表连接 避免使用right join 否则会容易搞不清楚怎么连接表格的

A left join B on xx left join C on yy

outer join也可以自连接

比如上面self join的代码中加上left 则可以将CEO的信息也输出,其manager为null

--多表外连接 Exercise  注意区分JOIN和LEFT JOIN的内部逻辑
SELECT
o.order_date,
o.order_id,
c.first_name,
s.name as shipper,
os.name AS status
FROM orders o 
JOIN customers c  
ON o.customer_id = c.customer_id
LEFT JOIN shippers s 
ON o.shipper_id = s.shipper_id
JOIN order_statuses os
ON o.status=os.order_status_id
ORDER BY status,o.order_id;

NATURAL JOIN 系统自己看着办:基于两个表相同的列连接 不建议使用

select * from orders o natural join customers c

CROSS JOIN 连接第一个表的每条记录和第二个表的每条记录 相互连接

select * from orders o cross join products p 显式

等价于 select * from orders, products 隐式交叉连接

  • USING

当两个表格中对应的列名相同时,可以使用USING等价替换ON语句 内外连接都可使用

ON o.customer_id = c.customer_id 等价于 USING (customer_id)

连接条件中添加多列(存在多个主键的情况) USING (order_id,product_id) 用,将相同的列隔开写入()

USING 后面的列名要加括号

  • UNION

合并多段查询记录 from同一个表或不同的表

--以2019年为分界点 查询订单状态 使用UNION
SELECT 
order_id,
order_date,
'Active' AS status      --这里直接添加一列属性,值全为字符串Active
FROM orders
WHERE order_date >='2019-01-01'
UNION
SELECT 
order_id,
order_date,
'Archived' AS status    
FROM orders
WHERE order_date <'2019-01-01';
四.第四章
  • INSERT INTO录入数据

按照create table时列的顺序录入属性值

当列的属性是AI auto_increment (一般是主键),使用DEFAULT 录入该属性

当列的属性不要求不为空NN,可以用NULL或DEFAULT录入该属性,则会自动补充为Default的内容

或可以自定义属性输入顺序 没有提到的属性会按照NULL或Default的值录入(见前面的笔记)

一次录入多行数据

--以shippers表格为例 列属性 shipper_id AI , name NN
INSERT INTO shippers(name)    -- 别的表格中有多个属性,按顺序添加
VALUES ('ZBQ'),               -- 一个括号中为一行数据
       ('AAA'),
       ('BBB');

插入分层行 (一次往多个表格中插入数据)

以store数据库为例,当存在一组订单时,会涉及到多个表格 orders order_items 母子关系

orders为母,order_items为子

orders表的一行可以在order_items中有一行或多行 一个订单可以有多个产品

LAST_INSERT_ID()

一个表格使用AUTO_INCREMENT(AI)属性为主键(PK)列生成唯一的整数值。当录入一行新数据到该表中时,MySQL会自动生成一个唯一的整数,并将其作为主键列的值。

通过使用 LAST_INSERT_ID函数获取生成的序列号,并此数字值使用在下一个语句中,例如:将其值作为新行的一个值插入到相关表中。

INSERT INTO orders (customer_id,order_date,status)
VALUES (1,'2019-10-02',1) ;  -- 添加一笔新的订单 使用customers表格中已存在的顾客id
-- SELECT LAST_INSERT_ID() 得到新插入数据的主键值 根据这个主键 到对应的表格中录入新数据
INSERT INTO order_items
VALUES (LAST_INSERT_ID(),5,100,3.12)
       (LAST_INSERT_ID(),6,10,1.11);  -- 一笔order数据 对应 两种产品
-- last_insert_id()这个位置的列 在属性中其实是AI,但这里说明也可以直接赋值
-- order_items这个表格有两个主键
  • 创建表复制 将一个表格复制到另外一个表格中 CREATE TABLE AS

CREATE TABLE orders_archived AS
SELECT * FROM orders      -- 表格中数据完全复制但是PK AI都没有设置    -- 属于subquery
INSERT INTO orders_archived    --使用选择语句作为插入语句中的子查询
SELECT *
FROM orders
WHERE order_date < '2019-01-01';
  • 更新数据 UPDATE table1 SET

更新数据可以直接赋值,也可以使用表达式,或直接赋列名

UPDATE invoices
SET
payment_total = invoice_total*0.5,
payment_date = due_date
WHERE invoice_id=3;    --where client_id in (3,4)   in的使用

改变where条件就可以多行修改 但是workbench会warning 因为设置中选了safe updates 一次只更新一条数据,防止意外更新或删除一些记录。warning不是报错不影响结果 建议还是把safe updates勾上

  • subquery :在另一段sql语句中的select语句

  • 恢复数据库: file——open SQL script 找到创建数据库的sql文件重新打开 全部执行

五.第五章
  • 聚合函数 一般用于数值型属性的列 也可以用于日期和字符串 如max用于日期 表示最近的日期

列中的空值NULL不会被聚合函数包含在内计算

--得到表格中所有记录条数 不管是不是空值 
select count(*) from table1;    -- 得到结果的排头会是 count(*) 所以一般还要as一下

聚合函数不止可以作用于列名上,也可以作用于表达式 比如 SUM(money_total*1.1)

select 
'First half of 2019' as data_range,
sum(invoice_total) as total_sales,
sum(payment_total) as total_payments,
sum(invoice_total)-sum(payment_total) as what_we_expect
-- 这里不能使用total_sales - total_payments 而是使用sum(total_sales-total_payments)
from invoices
where invoice_date between '2019-01-01' and '2019-06-30'
  • GROUP BY

默认状态下是按照GROUP BY中指定的列进行排序的

group by的列名不可以使用别名

  • HAVING 在GROUP BY 分组后进行筛选数据, 而WHERE是在分组前进行条件判断

HAVING中出现的列名必须出现在select之中 可以使用别名,但WHERE可以使用任何列名

-- 在分组后加入判断条件 (按照id分组求得各自的总销售并找到大于500的)
-- 但优先级:where > group by 但是还没有分组 不能确定按照分组求得的总销售额 所以where无法使用
select 
client_id,
SUM(invoices_total) as total_sales
from invoices
group by client_id
having total_sales>500;
  • WITH ROLLUP 运算符 只能应用于聚合值的列

如果用多列进行分组,则会对每一组的总计以及整个结果集都进行计算

select 
client_id,
SUM(invoices_total) as total_sales
from invoices
group by client_id 
with rollup;  -- 将total_sales那一列的总和 放在新增一行的对应列上  client_id对应列为NULL

(写exercise的时候注意 payment_method 还是payment_id 不然金额怎么都对不上)

六.第六章
  • 子查询subquery 不止可以在where中 也可以在select或from中

  • subquery vs joins

两种等价写法 --课堂上的例子  一开始没有写出来
select * from clients 
where client_id not in (
select distinct client_id from invoices
);

select * from clients 
join invoices using (client_id)
where client_id is NULL
  • ALL 表示所有

-- select invoices larger than max invoices of client_id =3
--原始方法
select * from invoices
where invoice_total >
(select max(invoice_total) from invoices where client_id =3);
-- 使用all关键字
select * from invoices
where invoice_total >
ALL(select invoice_total from invoices where client_id =3);  
-- 不加all时,select子查询语句中会得到不唯一的结果 
-- 加all 根据前面的大于号 大于all括号中的所有结果
  • SOME ANY 表示任一

-- 找出有至少两张发票的client_id  count和group by使用不熟练
select client_id
from invoices
group by client_id
having count(*) >=2 ; --聚合函数可以不在select列中  且在having中可以出现
  • correlated subqueries

-- 找出工资超过他们本部门平均工资的员工
select employee_id from 
(select * from employees
join
(select avg(salary) as avg ,office_id from employees
group by office_id)     -- 这里会报错 每一个派生出来的表都要有自己的别名 
using office_id )
where salary > avg

-- 正解  疯狂套娃
select employee_id from 
(select * from employees e1
join
(select avg(salary) as AVGsalary ,office_id from employees
group by office_id) as e2
using (office_id)) as e3
where salary > AVGsalary;

-- 关联子查询   子查询和外查询存在相关性   理解for循环
select employee_id
from employees e
where salary >(
    select avg(salary)
    from employees
    where office_id = e.office_id
)
  • exists运算符 where exits 类比 if判断语句

-- 找出有发票的客户
select * from clients c
where client_id in (
    select distinct client_id from invoices
)

select * from clients c
where exits(
    select client_id from invoices
    where client_id = c.client_id    --这里用了相关子查询 将内查询和外查询 关联
)
-- exits 并没有像in那样 返回一个结果集 而是 true\false
  • select中的子查询

-- average_invoices and differences
--错解
select invoice_id, 
invoice_total, 
avg(invoice_total) as average_invoices, 
invoice_total - avg(invoice_total) as differences   -- 这里不能使用average_invoices 别名
from invoices group by invoice_id;   -- 直接使用avg 一定要 group by 但要的是全体平均
-- Aggregate function in SELECT statement without GROUPBY will return one row only

--正解
select invoice_id, 
invoice_total, 
(select avg(invoice_total) from invoices) as average_invoices, 
invoice_total - (select average_invoices)    --不能直接使用别名 要么把子查询复制过来
                                           -- 要么select 别名
from invoices group by invoice_id;
-- 练习 ! 自己写出来哒 six!
select c.client_id, name,
(select sum(invoice_total) from invoices where client_id = c.client_id) as total_sales,
(select avg(invoice_total) from invoices) as average_invoices,
(select total_sales) - (select average_invoices) as differences  
   -- 这里可以直接 (select total_sales - avgrage_invoices)
from clients c;
  • from子句的子查询 在from中使用子查询 必须给其起一个别名

-- 可以将上一个练习得到的表格 用在from中
select * from (
    select c.client_id, name,
    (select sum(invoice_total) from invoices where client_id = c.client_id) as total_sales,
    (select avg(invoice_total) from invoices) as average_invoices,
    (select total_sales) - (select average_invoices) as differences  
       -- 这里可以直接 (select total_sales - avgrage_invoices)
    from clients c
) as sales_summary  
-- 这么长的from子查询 可以通过视图 保存在数据库中 后面会学
第七章.第七章

内置函数:用以处理数值,日期时间,字符串值

  • 数值函数

ROUND(num) 四舍五入数字 ROUND(num,a) a:四舍五入的精度 a取1 保留一位小数做四舍五入

TRUNCATE 截断数字 truncate(num,a) 从小数点后截取a位 如 5.1234 2 结果为5.12

CEILING FLOOR上(下)限函数 ceiling(num) floor(num)返回大于(小于) 等于这个数字的最小(大)整数

ABS(num) 绝对值

RAND(0,1) 生成0,1之间的随机数

  • 字符串函数

LENGTH('SDF') 字符串长度 UPPER() LOWER()

LTRIM RTRIM 移除字符串左(右)空白字符 TRIM 删除前后的空白字符

LEFT('abcd',2) 返回字符串左侧的几个字符 RIGHT 同

SUBSTRING('abcde',3,2) 得到一个字符串中任意位置的字符or串 3表示起始位置 2为长度

起始位置从1开始算 若第三个参数不写 则默认到字符串最后

LOCATE('aa','bbaaacc') 返回第一个参数(字符or字符串)的匹配位置

重复也返回出现的第一个位置 不区分大小写 找不到要匹配的字符 则返回0

REPLACE('abcdefg','abcd','zbq') 将第一个参数中出现的第二个参数替换为第三个参数

CONCAT 连接两个or多个字符串 concat(first_name,' ',last_name)

  • 日期和时间函数

NOW() 当前日期和时间 CURDATE() 当前日期 CURTIME() 当前时间 不要忘记()

一些用来提取特定日期或时间的构成元素

YEAR(NOW()) MONTH DAY HOUR MINITE SECOND同 返回结果为int

DAYNAME(NOW()) 返回星期几 字符串类型 MONTHNAME同

EXTRACT(DAY FROM NOW())

-- 查询当年的订单
select * from orders
where year(now())=year(order_date)
  • 时间和日期的格式化 改变时间日期的格式

DATE_FORMAT TIME_FORMAT

select date_format(now(),'%y')    
-- %y 表示两位的年份  %Y 表示4位的年份 %m 两位的月份 %M 月份的名称June %d 日期
select date_format(now(),'%m %d %y')
select time_format(now(),'%H:%i %p') 
-- %H小时 %i分钟  %p pm/am
  • 计算日期和时间

-- date_add 或者 date_sub  在现有日期上做出改变
select date_add(now(),interval 1 day)  -- 在现有日期上增加一天
                          -- interval 1 year 增加一年   
                          -- -1 减掉一年  或 使用 date_sub()
-- datediff 计算两个日期之间的间隔 返回天数
select datediff('2021-01-20','2023-01-20')   -- 前减后 结果 -730
-- time_to_sec 返回从0点计算的秒数
select time_to_sec('09:00')
  -- 计算时间间隔 返回秒数
select time_to_sec('09:00')-time_to_sec('09:02')   -- 结果 -120
  • 将NULL值换成某个内容 IFNULL(列名,'内容') 将空的列换成内容

或者 COALESEC(列名,'xxx','内容') 返回一堆值中第一个非空的的值

SELECT order_id,
ifnull(shipper_id,'not_assigned') as shipper
from orders;

SELECT order_id,
coalesec(shipper_id,comments,'not_assigned') as shipper  
-- comments是orders表中的一列 有的为空有的不为空
from orders;
  • IF IF(expression,first,second) expression 为true ,取first值 否则取second值

字符串 空值 日期 数字

-- 改写union那个部分的例子 
--以2019年为分界点 查询订单状态 使用UNION
SELECT 
order_id,
order_date,
IF(YEAR(order_date) = year(now()),'Active','Archived') AS status   
FROM orders;
-- exercise
select product_id,name from products
join
(select product_id, count(product_id)from order_items group by product_id)as table1
using(product_id) ;    -- ??????????为什么这里得不到join后的结果

   --使用if 再join   -- ??????? 母鸡
select product_id,name from products
join
(select product_id, count(product_id) as times,if(count(product_id)>1,'many','once') as status from order_items group by product_id) as aaa
using(product_id);      --这里要注意 if表达式中无法使用别名


-- 我的笨蛋方法  用到关联查询
select product_id,name,
    'many' as status 
    from products p 
where  
(select count(product_id) from order_items o where p.product_id=o.product_id )>1 
union
select product_id,name,
    'once' as status 
    from products p 
where  
(select count(product_id) from order_items o where p.product_id=o.product_id )=1 ;

-- 关联查询不止可以放在where中 直接放在select中 再结合if使用
select product_id,name,
    if((select count(product_id) from order_items o where p.product_id=o.product_id )>1 ,'many','once') as status
    from products p ;

-- 视频正解
select product_id ,name, count(*) as orders 
--, if balbala   
from products
join order_items using(product_id)
group by product_id,name;

CASE 运算符

当测试表达式有多个 if就不够用了

-- 今年的active 去年的last year 之前的archived
SELECT 
    order_id,
    CASE
       WHEN YEAR(order_date) = year(now()) THEN 'Active'
       WHEN YEAR(order_date) = year(now())-1 THEN 'LAST YEAR'
       WHEN YEAR(order_date) < year(now())-1 THEN 'Archived'
       ELSE 'Future'
    END AS status   -- 使用end关键字关闭case语句块
FROM orders;

--EXERCISE   金银铜   case的规则是符合即输出
CASE 
    WHEN points>=3000 THEN 'Active'
    WHEN points>2000 THEN 'Last year'  -- 不需要写>2000 <3000  到第二行已经能说明<3000了
END AS XX    
第八章 视图

将某段查询保存为视图,后面使用该查询时不用反复写

create view sales_by_client as
select 
    c.client_id,
    c.name
    SUM(invoices_total) as total_sales
from clients c
join invoices i
using (client_id) 
group by client_id,name
-- 在左侧 该数据库下的Views中,可以找到 sales_by_client 可以把它当作表格 select其中数据
-- 视图不存储数据  存储数据的是表格

你可能感兴趣的:(SQL学习,sql,数据库,mysql)