SQL之美(卷一)

160 阅读8分钟
DBMS数据库管理系统
Database Management System
1.5HDB 层次数据库
RDB 关系数据库
OOOB面向对象数据库
XMLDB
KVS 键值存储系统
2.RDBMS :Relational DBMS
有代表性的有5Oracle(甲骨文)
SQL Server(Microsoft)
DB2(IBM)
MySQL
PostgreSQL

SQL语句三剑客 关键字+表名+列名
DDL Data Definition Language 数据定义语言
指令:
CREATE DATABASE/TABLE
DROP DATABASE/TABLE
ALTER DATABASE/TABLE
DML Manipulation 操纵
SELECT
INSERT
UPDATE
DELETE (FROM)
DCL Control 控制
COMMIT
ROLLBACK
GRANT
REVOKE

SQL的书写规范
ANSI(美国国家标准协会)和ISO(国际标准化组织)每隔几年联合修订SQL的标准
关键字大写
表名首字母大写
列名等小写
常数: 字符串,日期,数字
字符串,日期需要用单引号括起来,AS别名时如为中文要用双引号
单词之间用半角空格或换行符分割

表的创建
1.约束可以在定义列时设置,也可以在语句的末尾设置(NOT NULL约束只能以列为单位设置)
2.product_id不能写作product-id,标准SQL不允许使用连字符- 作为列名等名称

常用4种数据类型
INTEGER 
    存储整型
CHAR(n) 
    定长字符串,不够n需要用半角空格补足
    n是可以存储的字符串的最大长度,中文占两到三个字符,由编码集定
VARCHAR (Oracle中为VARCHAR2)
    可变长字符串
DATE 
    存储年月日(OracleDATE还包含时分秒)

表定义的修改
ALTER TABLE <表名> ADD/DROP COLUMN <列的定义>
列的定义: 列名+类型+约束
(OracleSqlServer中省略COLUMN)
变更表名
OracleSqlServer:ALTER TABLE p1 RENAME TO p2
MySQL:RENAME TABLE p1 to p2

匹配查询(query)
子句(clause)以SELECTFROM等关键字作为起始的短语,书写时以子句为单位进行换行

删除重复行DISTINCT
 1.只能写在第一个列名前
 2.可以查询出NULL,NULL被视为一类
 
 Oracle查询语句中不允许省略FROM子句,要用dual临时表
 SELECT 常数
 FROM dual
 
 比较运算符
 1.可以对几乎所有数据类型做比较,字符串的比较是按照字典序的
 2.不能对NULL进行比较,它有特定的语法: IS (NOT) NULL
 3.NULL / 0 结果为NULL ; 5 / 0 报错
 4.标准SQL里 不等于是 <> 而不是 !=
 
 逻辑运算符 NOT AND OR
 1. 对比较运算符等返回的真值进行操作
 2. 真值表(true table)  若真为1,假为0,则 AND称为逻辑积,OR称为逻辑和
 3.SQL的逻辑运算称为三值逻辑,TRUEFALSE,UNKNOWN
 4.WHERE NOT price >1000  等同于 WHERE price <=1000
 5.AND优先于OR执行,可通过括号强化处理
 
 聚合查询
 1.count(*) *星号代表所有的行,count(列名)会得到NULL之外的数据行数
 2.所有的聚合函数,如果以列名为参数,那么在计算之前就已经把NULL排除在外了
 3.SUM/AVG只能对数值类型的列使用,MAX/MIN适用于所有数据类型
 4.count(distinct 列名) 计算行数前会删除指定列的重复数据
 5.SELECT子句,HAVING子句可以指定的元素:常数,聚合函数,GROUP BY子句中指定的列名(称为聚合建);
 ORDER BY子句可以使用SELECT子句中未出现的列或聚合函数,可以使用SELECT子句中定义的列的别名
 MySQL方言:下面语句是可以执行的
     select c1,c2,count(*)
     from <表名>
     group by c1;
 6.可以使用聚合函数的子句:
     SELECT
     HAVING
     ORDER BY
 7.聚合操作时,DBMS内部会进行排序,只有尽可能减少排序的行数,才能提高处理速度
 优化:在排序前通过WHERE过滤掉不用的数据,并为WHERE指定条件的列创建索引
 8.ORDER BY 排序键中有NULL值时,会出现在结果集的开头或结尾
 ASC(默认)ascendent上升的
 DESC descendent下降的
 
 数据插入
 1.insert into <表名> (列1,列2,列3...)values(值1,值2,值3...)
 列1,列2,列3... 称为列清单
 值1,值2,值3... 称为值清单
 2.多行insert (multi row)
 insert into <表名> (列1,列2,列3...)values(值清单1),(值清单2),(值清单3);
 3.全列insert时,可省略列清单
 4.插入NULL时,显式传入NULl
 5.插入默认值DEFAULT;隐式插入默认值,列清单里省略该列
 6.INSERT...SELECT... 复制一张表
 
 数据删除
 1.DELETE FROM <表名> (WHERE<条件>) 指定WHERE时称为搜索型DELETE
 2.TRUNCATE清空表,效率更高
 
数据更新
UPDATE <表名>
SET <列名1>=<表达式1>,<列名2>=<表达式2>
where <条件>
 
事务transaction
1.一个处理单元中执行的一系列更新处理的结合
2.ACID 
原子性atomicity 要么全部执行,要么完全不执行
隔离性isolation 事务之间在没有提交之前,是无法查看到其他事务操作后的数据的
一致性consistency 不合法的SQL会回滚,要满足定义好的约束
持久性durability 执行记录保存在硬盘,也就是日志,可以恢复到某个时间点
3.默认为自动提交模式(每条SQL语句就是一个事务)
4.数据库连接时事务已经开始

视图VIEW
1.保存的只是SELECT语句,不保存数据,节省了磁盘空间
2.CERATE VIEW <视图名>(列名1,列名2)AS SELECT ...
3.通常将用来校验数据的SQL保存在视图里,直接SELECT * 查看
4.尽量使用单一视图,而不是多重视图
5.不能使用ORDER BY
6.视图的更新(略)

子查询
1.一次性视图:子查询在SELECT语句执行完后就会消失
2.子查询需要取别名
3.标量子查询 scalar subquery,标量:单一
  结果是一行一列的,故可以比较运算符中
  标量子查询的书写位置:能够使用常数或列名的位置
4.关联子查询 使用场景:组内进行比较时
 结合条件写在子查询的WHERE子句中
 SELECT xxx
 FROM product p1
 WHERE price >(
 SELECT avg(price)
 FROm product p2
 WHERE p1.type=p2.type
 GROUP BY type
 );
 
 SELECT xxx,
        (SELECT avg(price)
         FROM product P2
         WHERE p1.type=p2.type
         GROUP BY type
        ) AS avg_type
 FROM product p1;
 

 函数(函数中的函就是盒子:输入值经过盒子后得到输出结果)
 根据用途分类:
 算术函数
 ABS(列名或常数):绝对值absolute value,不考虑数值的符号,表示一个数到远点的距离
 MOD(被除数,除数):取余modulo,这里被除数和除数可以是列名或常数
 ROUND(列名或常数,保留小数的位数)四舍五入,对指定保留位数的后一位做四舍五入
 SELECT ROUND(27.34,1); -- 27.3
 SELECT ROUND(27.345,2); -- 27.5
 字符串函数
 拼接字符串 c1 || c2 ||c3
 MySQL CONCAT(c1,c2,c3)
 SqlServer  c1+c2+c3
 长度 LENGTH(c1) 计算字符串c1包含多少个字符,汉字占两个字符,JAVA中一个char类型占2个字节,String类型里一个字符等于1个字节,汉字占2个字节
 SqlServer: LEN()
 LOWER()
 UPPER()
 替换:REPLACE(c1,str1,str2) 在c1中将str1替换成str2
 截取:
 MySQL: SUBSTRING(c1 FROM i FOR j) 在c1中从offset为i的位置开始截取j个字符
SqlServer: SUBSTRING(c1,i,j)
Oracle: SUBSTR(c1,i,j)
 日期函数
 CURRENT_DATE 年月日 (无参数,不需要带括号)
 ORACLE: SELECT CURRENT_DATE FROM dual
 SqlServer:  CAST(CURRENT_TIMESTAMP AS DATE)
 CURRENT_TIME 时分秒
  SqlServer:  CAST(CURRENT_TIMESTAMP AS TIME)
  CURRENT_TIMESTAMP 日期+时间
  截取日期EXTRACT(日期元素 FROM 日期)
  EXTRACT(YEAR/MONTH/DAY FROM CURRENT_TIMESTAMP)
  SqlServer: DATEPART(YEAR,CURRENT_TIMESTAMP)
 转换函数
 CAST 类型转换
 CAST('2021-10-26' AS DATE)
 将NULL值转换为其他值COALESCE() 返回第一个非NULL的值
 聚合函数
 
 谓语predicate
 1.返回值为真值得函数
 2.比较运算符,正式的名字就是比较谓语
 3.LiKE 是否包含
 LIKE '%dart%'
 LIKE '_dart_'
 4.范围查询 BETWEEN...AND...
 5.IS(NOT)NULL
 6.(NOT)IN 替换OR
 IN的结果集中如有NULL,则整个SELECT子句返回NULL
 IN通常跟子查询
 7.(NOT)EXISTS 左侧无参数,只关心是否有记录存在
 SELECT xxx
 FROM product p1
 WHERE EXISTS (
 SELECT * 
 FROM order o1 
 WHERE o1.product_id=p1.id
 AND o1.payment='2021-10-26'
 )
 
 CASE表达式(多行转一行)
 1.条件分支
 2.Oracle: DECODE;MySQL:IF
 3.ELSE子句可省略不写,默认是ELSE NULL
 4.两种写法:区别:简单CASE表达式只能对一列做判断
 搜索型CASE表达式:
  CASE  WHEN   <表达式> THEN  <表达式>
        WHEN   <表达式> THEN  <表达式>
  ELSE <表达式>
  END
 简单CASE表达式
 CASE <表达式>
    WHEN   <表达式> THEN  <表达式>
    WHEN   <表达式> THEN  <表达式>
 ELSE <表达式>
 END
 5.SUM(CASE xx WHEN xx THEN xx ELSE 0 END)
 
 集合运算:表的加减法
 1.UNION(表的加法),
 INTERSECT(交集),
 EXCEPT(表的减法):
    Oracle:MINUS
    MySQL不支持EXCEPT
 2.两表的列数必须相同
 3.包含重复行时带ALL选项
 4.ORDER BY子句只能在最后一次使用
 5.UNION ALL不需要去重,所以不会对结果集排序,性能好于UNION
 
 案例:选取掌握所有3个领域的技术的员工
 SELECT DISTINCT emp
 FROM Emp t1
 WHERE NOT EXISTS(
    SELECT skill
    FROM skills
    EXCEPT
    SELECT skill
    FROM emp t2
    WHERE t1.emp=t2.emp
 )
 
 联结
 内联结 INNER JOIN
 外联结 OUTER JOIN
 交叉联结CROSS JOIN笛卡尔积(表的乘法)

过时语法的内联结
SELECT t1.product_name,t1.price,t2.amount
FROM product t1,order t2
WHERE t1.id=t2.product_id
AND t2.order_id='1001';
这样的语法不推荐使用的原因:
1.无法马上判断出是内,外联结
2.无法分辨出WHERE子句里哪部分是联结条件,哪部分是过滤条件
3.后续可能废弃这样的语法


窗口函数 OVER( PARTITION By <列清单> ORDER BY <列清单>)
1.能够作为窗口函数的聚合函数(SUM,AVG,COUNT,MAX,MIN)
2.RANK,ROW_NUMBER,DENSE_RANK等专用窗口函数(没有参数)
3.窗口函数不具备GROUP BY子句的汇总功能,因此不能减少记录行数
4.有相同位次时,排序不同 RANK: 1 1 3;DENSE_RANK: 1 1 2;ROW_NUMBER: 1 2 3
5. 移动平均 moving average(都是相对当前行的位置)
ORDER BY1 ROWS n PRECEDING: 前n行,带上当前行也就是最近的(n+1)行
ORDER BY1 ROWS n FOLLOWING: 后n行,带上当前行也就是此后的(n+1)行
ORDER BY1 ROWS BETWEEN m  PRECEDING AND n FOLLOWING: 前m行到后n行
6. 如果想让结果集全局有序,则在结尾处使用ORDER BY子句
SELECT id,RANK(ORDER BY dt) AS RANKING
FROM t1
ORDER BY dt;

其他运算符
1.合计和小计:ROLLUP 卷起
一次计算出不同聚合键组合的结果
GROUP BY ROLLUP(c1,c2)
MySQL:GROUP BY t WITH ROLLUP
 其实等同于
 GROUP BY ()  默认使用NULL作为聚合键,聚合的结果其实相当于未分组的总值.超级分组 super group row
 GROUP BY c1
 GROUP BY (c1,c2)
 2.GROUPING(聚合键) 超级分组产生的NULL则返回1,其他为0
 3.CUBE:  2^n
  ROLLUP: n+1
 4.GROUPING SETS 指定聚合键组合
 
一条SQL语句可以操作多行数据.但是像JAVA这样的程序语言来说,数据访问是逐行进行的,要想处理多行数据,就需要循环遍历