您的位置:首页 > 数据库

《SQL入门经典》学习笔记

2016-07-22 16:10 471 查看
第一部分  SQL概念综述

第1章  欢迎来到SQL世界

1.4.1 表命名标准

表的名称以_TBL作为后缀,表的索引以_INX为后缀

1.4.3 表的构成

字段是表里的一列,记录是表里的一行

第二部分  建立数据库

第2章  定义数据结构

2.2.1 定长字符串

CHARACTER(n): n为最大字符数,通常用空格来填充数量不足的字符

2.2.2 变长字符串

CHARACTER VARYING(n): n为最大字符数

2.2.5 小数类型

DECIMAL(p,s): p为有效位数,s表示标度,即小数点后面的位数

2.2.7 浮点数

REAL为单精度浮点数,有效位数为1~21,DOUBLE PRECISION为双精度浮点数,有效位数为22~53

2.2.9 直义字符串

直义字符串为用户或程序明确指定的一系列字符,一般来说,字符型字符串需要使用单引号

2.2.10 NULL数据类型

NULL表示没有值

2.2.12 自定义类型

CREATE TYPE PERSON AS OBJECT

(NAME VARCHAR (30),

SSN   VARCHAR (9)); # 创建PERSON类型

2.2.13 域

CREATE DOMAIN MONEY_D AS NUMBER(8,2); # 创建域MONEY_D, 域类似于自定义类型,但可以向域添加约束

ALTER MONEY_D

ADD CONSTRAINT MONEY_CON1

CHECK (VALUE > 5);

第3章  管理数据库对象

3.2 什么是规划

规划是与数据库某个用户名相关联的数据库对象的集合

3.3.3 CREATE TABLE语句

CREATE TABLE EMPLOYEE_TBL

(EMP_ID      CHAR(9)      NOT NULL,

EMP_NAME     VARCHAR(40)  NOT NULL,

EMP_ST_ADDR  VARCHAR(20)  NOT NULL,

EMP_CITY     VARCHAR(15)  NOT NULL,

EMP_ST       CHAR(2)      NOT NULL,

EMP_ZIP      INTEGER(5)   NOT NULL,

EMP_PHONE    INTEGER(10)  NULL,

EMP_PAGER    INTEGER(10)  NULL); # 创建表EMPLOYEE_TBL, NOT NULL表示不能为空,NULL表示可以为空

3.3.5 ALTER TABLE命令

ALTER TABLE EMPLOYEE_TBL MODIFY

EMP_ID VARCHAR(10); # 将EMP_ID的类型修改为VARCHAR(10)

如果表已经包含数据,这时添加的列就不能定义为NUT NULL,这是一条基本规则

CREATE TABLE TEST_INCREMENT(

ID         SERIAL,

TEST_NAME  VARCHAR(20)); # SERIAL为自动增加的数值,

INSERT INTO TEST_INCREMENT(TEST_NAME)

VALUES ('FRED'), ('JOE'), ('MIKE'), ('TED'); # 向表中插入记录,而不用为自动增加的列指定值

3.3.6 从现有表新建另一个表

CREATE TABLE PRODUCTS_TMP AS

SELECT * FROM PRODUCTS_TBL; # 从表PRODUCTS_TBL新建表PRODUCTS_TMP

3.3.7 删除表

DROP TABLE PRODUCTS_TMP; # 删除表PRODUCTS_TMP, 如果使用了RESTRICT选项,且表被视图或约束所引用,就会返回一个错误,如果使用了CASCADE选项,则全部引用视图和约束都被删除

3.4.1 主键约束

主键是表里一个或多个用于实现记录唯一性的字段

CREATE TABLE EMPLOYEE_TBL

(EMP_ID      CHAR(9)      NOT NULL    PRIMARY KEY,

EMP_NAME     VARCHAR(40)  NOT NULL,

EMP_ST_ADDR  VARCHAR(20)  NOT NULL,

EMP_CITY     VARCHAR(15)  NOT NULL,

EMP_ST       CHAR(2)      NOT NULL,

EMP_ZIP      INTEGER(5)   NOT NULL,

EMP_PHONE    INTEGER(10)  NULL,

EMP_PAGER    INTEGER(10)  NULL); # 创建表EMPLOYEE_TBL时指定EMP_ID为主键

3.4.2 唯一性约束

唯一性约束要求表里某个字段的值在每条记录里都是唯一的,与主键类似

CREATE TABLE EMPLOYEE_TBL

(EMP_ID      CHAR(9)      NOT NULL    PRIMARY KEY,

EMP_NAME     VARCHAR(40)  NOT NULL,

EMP_ST_ADDR  VARCHAR(20)  NOT NULL,

EMP_CITY     VARCHAR(15)  NOT NULL,

EMP_ST       CHAR(2)      NOT NULL,

EMP_ZIP      INTEGER(5)   NOT NULL,

EMP_PHONE    INTEGER(10)  NULL        UNIQUE,

EMP_PAGER    INTEGER(10)  NULL); # 创建表EMPLOYEE_TBL时指定EMP_PHONE为UNIQUE,表示任意两个雇员不能有相同的电话

3.4.3 外键约束

外键是子表里的一个字段,引用父表里的主键

CREATE TABLE EMPLOYEE_PAY_TBL

(EMP_ID         CHAR(9)      NOT NULL,

POSITION        VARCHAR2(15) NOT NULL,

DATE_HIRE       DATE         NULL,

PAY_RATE        NUMBER(4,2)  NOT NULL,

DATE_LAST_RAISE DATE         NULL,

CONSTRAINT EMP_ID_FK FOREIGN KEY (EMP_ID) REFERENCES EMPLOYEE_TBL (EMP_ID)); # EMP_ID_FK为外键约束名,将EMP_ID定义为外键,且引用表EMPLOYEE_TBL里的EMP_ID字段

3.4.5 检查约束

CREATE TABLE EMPLOYEE_PAY_TBL

(EMP_ID         CHAR(9)      NOT NULL,

POSITION        VARCHAR2(15) NOT NULL,

DATE_HIRE       DATE         NULL,

PAY_RATE        NUMBER(4,2)  NOT NULL,

DATE_LAST_RAISE DATE         NULL,

CONSTRAINT EMP_ID_FK FOREIGN KEY (EMP_ID) REFERENCES EMPLOYEE_TBL (EMP_ID),

CONSTRAINT CHK_PAY CHECK ( PAY_RATE > 12.50)); # CHK_PAY为检查约束名,表示PAY_RATE不得低于12.50

3.4.6 去除约束

ALTER TABLE EMPLOYEES

DROP CONSTRAINT EMPLOYEES_PK; # 去除表EMPLOYEES中的约束EMPLOYEE_PK

3.7.2 练习

$ mysql -h localhost -u username -ppassword # -p与密码之间没有空格

CREATE DATABASE learnsql; # 建立数据库learnsql

SHOW DATABASES; # 显示所有数据库

USE learnsql; # 使用learnsql数据库

SHOW TABLES; # 显示所有的表

DESCRIBE EMPLOYEE_TBL; # 列出表EMPLOYEE_TBL的所有字段和它们的属性

DROP TABLE ORDER_TBL; # 删除表ORDER_TBL

第4章  规格化过程

规格化是把原始数据分解为由关联数据形成的多个表

4.1.3 规格形式

第一规格形式的目标是把原始数据分解到表中

第二规格形式的目标是提取对主键仅有部分依赖的数据,把它们保存到另一个表里

第三规格形式的目标是删除表里不依赖主键的数据

第5章  操作数据

5.2.1 把数据插入到表

INSERT INTO PRODUCTS_TBL

VALUES ('7725', 'LEATHER GLOVES', 24.99); # 把一条新记录插入到表PRODUCTS_TBL里

5.2.2 给表里指定列插入数据

INSERT INTO ORDERS_TBL (ORD_NUM, CUST_ID, PROD_ID, QTY)

VALUES ('23A16', '109', '7725', 2); # 向表ORDER_TBL里的某些列插入数据

5.2.3 从另一个表插入数据

INSERT INTO PRODUCTS_TMP

SELECT * FROM PRODUCTS_TBL; # 将PRODUCTS_TBL中的数据插入到PRODUCTS_TMP中

5.2.4 插入NULL值

INSERT INTO ORDERS_TBL (ORD_NUM, CUST_IN, PROD_ID, QTY, ORD_DATE)

VALUES ('23A16', '109', '7725', 2, NULL); # ORD_DATE字段被插入NULL

5.3.1 更新一个字段的数据

UPDATE ORDERS_TBL

SET QTY = 1

WHERE ORD_NUM = '23A16'; # 把表ORDERS_TBL里ORD_NUM值为'23A16'的记录中QTY更新为1

5.3.2 更新一条或多条记录里的多个字段

UPDATE ORDERS_TBL

SET QTY = 1, CUST_ID = '221'

WHERE ORD_NUM = '23A16'; # 把表ORDERS_TBL里ORD_NUM值为'23A16'的记录中QTY更新为1,CUST_ID更新为221

5.4 从表里删除数据

DELETE FROM ORDERS_TBL

WHERE ORD_NUM = '23A16'; # 从表ORDERS_TBL中删除ORD_NUM为'23A16'的一行记录

# 作为一条规则,DELETE语句应该总是使用WHERE子句,另外,还应该首先使用SELECT语句对WHERE子句进行测试

第6章  管理数据库事务

6.1 什么是事务

事务是对数据库执行的一个操作单位,可以是一个或多个DML(数据操作语言)语句

START TRANSACTION; # mariadb数据库中开始事务

6.2.1 COMMIT命令

COMMIT; # 把修改保存到数据库,完成这个事务

6.2.2 ROLLBACK命令

ROLLBACK; # 撤销还没有被保存到数据库的命令,只用于撤销上一个COMMIT或ROOLBACK命令之后的事务

6.2.3 SAVEPOINT命令

SAVEPOINT sp1; # 创建保存点sp1,保存点是事务过程中的一个逻辑点,我们可以把事务回退到这个点,而不必回退整个事务

6.2.4 ROOLBACK TO SAVEPOINT命令

ROOLBACK TO sp1; # 回退到名为sp1的保存点

6.2.5 RELEASE SAVEPOINT命令

RELEASE SAVEPOINT sp1; # 删除保存点sp1

6.2.6 SET TRANSACTION命令

SET TRANSACTION READ WRITE; # 初始化数据库事务,进行查询和操作

SET TRANSACTION READ ONLY; # 初始化数据库事务,只进行查询,适合生成报告,能够提高事务完成的速度

第三部分  从查询中获得有效的结果

第7章 数据库查询

7.2.1 SELECT 语句

SELECT * FROM PRODUCTS_TBL; # 对表PRODUCTS_TBL进行查询,*表示表里的全部字段

SELECT PROD_DESC FROM CANDY_TBL; # 显示表CANDY_TBL里的PROD_DESC字段

SELECT DISTINCT PROD_DESC

FROM CANDY_TBL; # DISTINCT选项指定显示中去除重复记录

SELECT DISTINCT (PROD_DESC)

FROM CANDY_TBL; # 用圆括号以提高代码的可读性

7.2.3 WHERE子句

SELECT * FROM PRODUCTS_TBL

WHERE COST < 5; # 显示价格小于5的记录

7.2.4 ORDER BY子句

SELECT PROD_DESC, PROD_ID, COST

FROM PRODUCTS_TBL

WHERE COST < 20

ORDER BY PROD_DESC ASC; # 以字段PROD_DESC按升序排列,ASC为升序,DESC为降序,默认为升序

SELECT PROD_DESC, RPOD_ID, COST

FROM PRODUCTS_TBL

WHERE COST < 20

ORDER BY 1; # 1代表字段PROD_DESC, 2代表字段PROD_ID, 3代表字段COST

7.2.5 大小写敏感性

一般来说,SQL命令和关键字不区分大小写,而数据的大小写敏感性则取决于所用的数据库

7.3.1 统计表里的记录数量

SELECT COUNT (*) FROM PRODUCTS_TBL; # 统计表PRODUCTS_TBL里的记录数量

SELECT COUNT (PROD_ID) FROM PRODUCTS_TBL; # 统计表PRODUCTS_TBL里字段PROD_ID的值的数量

SELECT COUNT (DISTINCT PROD_ID) FROM PRODUCTS_TBL; # 统计表PRODUCTS_TBL里字段PROD_ID中不同值的种类数

7.3.2 从另一个用户表里选择数据

SELECT EMP_ID

FROM SCHEMA.EMPLOYEE_TBL; # 对用户SCHEMA的表EMPLOYEE_TBL进行查询

7.3.3 使用字段别名

SELECT PROD_DESC PRODUCT

FROM PRODUCTS_TBL; # 给字段PROD_DESC定义别名为PRODUCT

第8章  使用操作符对数据进行分类

操作符用于在SELECT命令的WHERE子句中为返回的数据指定更明确的条件

8.2.1 相等

SELECT *

FROM PEODUCTS_TBL

WHERE PROD_ID = '2345'; # 返回PROD_ID等于2345的记录

8.2.2 不等于

SELECT *

FROM PRODUCTS_TBL

WHERE PROD_ID <> '2345'; # 返回PROD_ID不等于2345的记录

8.2.3 小于和大于

SELECT *

FROM PRODUCTS_TBL

WHERE COST > 20; # 返回COST大于20的记录

8.3.1 IS NULL

SELECT *

FROM EMPLOYEE_TBL

WHERE PAGER IS NULL; # 返回PAGER为NULL的记录,注意单词'NULL'与NULL值是不同的

8.3.2 BETWEEN

SELECT *

FROM PRODUCTS_TBL

WHERE COST BETWEEN 5.59 AND 14.5; # 返回COST在5.95与14.5之间的记录

8.3.3 IN

SELECT *

FROM PRODUCTS_TBL

WHERE PROD_ID IN ('13', '9', '87', '119'); # 返回PROD_ID在指定范围内的记录

8.3.4 LIKE

SELECT PROD_DESC

FROM PRODUCTS_TBL

WHERE PROD_DESC LIKE '_S%'; # 返回PROD_DESC的第二个字符是S的记录,%代表零个、一个或多个字符,下划线_代表一个字符

8.3.5 EXISTS

SELECT COST

FROM PRODUCTS_TBL

WHERE EXISTS ( SELECT COST

               FROM PRODUCTS_TBL

               WHERE COST < 100 ); # EXISTS用于搜索指定表里是否存在满足特定条件的记录

8.3.6 ALL、SOME和ANY操作符

SELECT *

FROM PRODUCTS_TBL

WHERE COST > ALL ( SELECT COST

                   FROM PRODUCTS_TBL

                   WHERE COST < 10 ); # ALL用于把一个值与另一个集合里的全部值进行比较,即要求比其中所有值都大

SELECT *

FROM PRODUCTS_TBL

WHERE COST > ANY ( SELECT COST

                   FROM PRODUCTS_TBL

                   WHERE COST < 10 ); # ANY用于把一个值与另一个集合里的任意值进行比较,即只要求比其中任意一个大即可

SOME是ANY的别名,它们可以互换

8.4.1 AND

SELECT *

FROM PRODUCTS_TBL

WHERE COST > 10

AND COST < 30; # 返回10<COST<30的记录

8.4.2 OR

SELECT *

FROM PRODUCTS_TBL

WHERE PROD_ID = '90'

OR PROD_ID = '2345'; # 返回PROD_ID等于90或2345的记录

8.5.1 不相等

WHERE SALARY <> '20000'; # 不等于

WHERE SALARY != '20000'; # 不等于,两者等价

8.5.2 NOT BETWEEN

SELECT *

FROM PRODUCTS_TBL

WHERE COST NOT BETWEEN 5.95 AND 14.5; # 返回COST不在5.95和14.5之间且不包含5.95和14.5的记录

8.5.3 NOT IN

SELECT *

FROM PRODUCTS_TBL

WHERE PROD_ID NOT IN (119, 13, 87, 9); # 返回PROD_ID不在列表里的记录

8.5.4 NOT LIKE

SELECT *

FROM PRODUCTS_TBL

WHERE PROD_DESC NOT LIKE 'L%'; # 返回PROD_DESC不是以L开头的记录

8.5.5 IS NOT NULL

SELECT *

FROM EMPLOYEE_TBL

WHERE PAGER IS NOT NULL; # 返回PAGER不是空的记录

8.5.6 NOT EXISTS

SELECT MAX(COST)

FROM PRODUCTS_TBL

WHERE NOT EXISTS ( SELECT COST

                   FROM PRODUCTS_TBL

                   WHERE COST > 100 ); # 返回COST不是大于100的最大值

8.6.1 加法

SELECT SALARY + BONUS

FROM EMPLOYEE_PAY_TBL;

8.6.2 减法

SELECT SALARY - BONUS

FROM EMPLOYEE_PAY_TBL;

8.6.3 乘法

SELECT EMP_ID, PAY_RATE, PAY_RATE * 1.1

FROM EMPLOYEE_PAY_TBL

WHERE PAY_RATE IS NOT NULL;

8.6.4 除法

SELECT SALARY / 10 FROM EMPLOYEE_PAY_TBL;

第9章  汇总查询得到的数据

9.1.1 COUNT函数

SELECT COUNT(EMPLOYEE_ID) FROM EMPLOYEE_PAY_ID; # 统计EMPLOYEE_ID字段,不包括NULL

SELECT COUNT(DISTINCT DALARY) FROM EMPLOYEE_PAY_TBL; # 只统计不相同的行

SELECT COUNT(*) FROM EMPLOYEE_TBL; # 统计表里的全部记录,包括重复项和NULL

9.1.2 SUM函数

SELECT SUM(SALARY) FROM EMPLOYEE_PAY_TBL; # 统计SALARY字段的总和

9.1.3 AVG函数

SELECT AVG(SALARY) FROM EMPLOYEE_PAY_TBL; # 返回SALARY字段的平均值

9.1.4 MAX函数

SELECT MAX(SALARY) FROM EMPLOYEE_PAY_TBL; # 返回SALARY字段的最大值,NULL不在统计之内,字符数据按排序规则统计最大值

9.1.5 MIN函数

SELECT MIN(SALARY) FROM EMPLOYEE_PAY_TBL; # 返回SALARY字段的最小值,NULL不在统计之内

第10章  数据排序与分组

10.2.3 创建分组和使用汇总函数

SELECT CITY, COUNT(*)

FROM EMPLOYEE_TBL

GROUP BY CITY; # 以CITY分组,统计每个CITY的记录数量

10.4 CUBE和ROLLUP语句

SELECT CITY, ZIP, AVG(PAY_RATE), AVG(SALARY)

FROM EMPLOYEE_TBL E INNER JOIN EMPLOYEE_PAY_TBL P

ON E.EMP_ID = P.EMP_ID

GROUP BY CITY, ZIP WITH ROLLUP; # 使用ROLLUP语句来获得小计(mysql语法)

SELECT CITY, ZIP, AVG(PAY_RATE), AVG(SALARY)

FROM EMPLOYEE_TBL E INNER JOIN EMPLOYEE_PAY_TBL P

ON E.EMP_ID = P.EMP_ID

GROUP BY CUBE(CITY, ZIP); # CUBE对分组列表中的所有字段进行排列组合,并根据每一种组合结果,分别进行统计汇总

10.5 HAVING子句

SELECT CITY, AVG(PAY_RATE), AVG(SALARY)

FROM EMPLOYEE_PAY_TBL

WHERE CITY <> 'GREENWOOD'

GROUP BY CITY

HAVING AVG(SALARY) > 20000

ORDER BY 3; # 只显示AVG(SALARY)>20000的分组

第11章  调整数据的外观

11.2.1 串接函数

SELECT CONCAT(FIRST_NAME, ' ', LAST_NAME) NAME

FROM EMPLOYEE_TBL; # 将FIRST_NAME和LAST_NAME串接在一起(mysql语法)

11.2.2 TRANSLATE函数

SELECT CITY, TRANSLATE(CITY, 'IND', 'ABC')

FROM EMPLOYEE_TBL; # 将CITY中的I替换为A,将N替换为B,将D替换为C

11.2.3 REPLACE函数

SELECT CITY, REPLACE(CITY, 'I', 'Z')

FROM EMPLOYEE_TBL; # 把CITY中的I替换为Z,REPLACE与TRANSLATE类似,只是它替换的是一个字符或字符串

11.2.4 UPPER函数

SELECT UPPER(CITY)

FROM EMPLOYEE_TBL; # 将CITY中的所有字符都转化为大写

11.2.5 LOWER函数

SELECT LOWER(CITY)

FROM EMPLOYEE_TBL; # 将CITY中的所有字符都转化为小写

11.2.6 SUBSTR函数

SELECT EMP_ID, SUBSTRING(EMP_ID, 1, 3)

FROM EMPLOYEE_TBL; # 返回EMP_ID的前1个字符

11.2.7 INSTR

SELECT PROD_DESC, INSTR(PROD_DESC, 'A', 1, 1)

FROM PRODUCTS_TBL; # 在PROD_DESC中从第1个字符开始查找A第1次出现的位置

11.2.8 LTRIM函数

SELECT POSITION, LTRIM(POSITION, 'SALES')

FROM EMPLOYEE_PAY_TBL; # LTRIM从左侧剪除POSITION中的SALES,例SHIPPER->HIPPER, SALE被忽略

11.2.9 RTRIM函数

同上

11.2.9 DECODE函数

SELECT CITY, DECODE(CITY, 'INDIANAPOLIS', 'INDY', 'GREENWOOD', 'GREEN', 'OTHER')

FROM EMPLOYEE_TBL; # 将CITY中的INDIANAPOLIS显示为INDY,GREENWOOD显示为GREEN,其他显示为OTHER

11.3.1 LENGTH函数

SELECT PROD_DESC, LENGTH(PROD_DESC)

FROM PRODUCTS_TBL; # 返回PROD_DESC的字符长度

11.3.2 IFNULL函数

SELECT PAGER, IFNULL(PAGER, 999999999)

FROM EMPLOYEE_TBL; # 若PAGER为NULL则用999999999代替

11.3.3 COALESCE函数

SELECT EMP_ID, COALESCE(BONUS, SALARY, PAY_RATE)

FROM EMPLOYEE_PAY_TBL; # 返回BONUS, SALARY, PAY_RATE字段里第一个非NULL值

11.3.4 LPAD函数

SELECT LPAD(PROD_DESC, 30, '.') PRODUCT

FROM PRODUCTS_TBL; # 在PROD_DESC左侧添加句点,使其总长度达到30个字符

11.3.5 RPAD函数

同上

11.3.6 ASCII函数

ASCII('A')返回65;

ASCII('a')返回95

11.4 算术函数

ABS:绝对值

ROUND:舍入

SQRT:平方根

SIGN:符号

POWER:幂

CEIL:上限

FLOOR:下限

EXP:指数

SIN、COS、TAN:三角函数

11.5.1 字符串转换为数字

SELECT EMP_ID, TO_NUMBER(EMP_ID)

FROM EMPLOYEE_TBL; # 在输出结果里,数值是右对齐的,字符串是左对齐的

11.5.2 数字转换为字符串

SELECT PAY_RATE, TO_CHAR(PAY_RATE)

FROM EMPLOYEE_PAY_TBL

WHERE PAY_RATE IS NOT NULL;

第12章  日期和时间

12.1.2 DATETIME元素

DATETIME元素  有效范围

YEAR          0001-9999

MONTH         01-12

DAY           01-31

HOUR          00-23

MINUTE        00-59

SECOND        00.000...-61.999...

12.2.1 当前日期

SELECT NOW(); # 返回当前日期和时间(mysql语法)

SELECT CURRENT_DATE; # 返回当前日期

12.2.3 时间与日期相加

SELECT DATE_HIRE, DATE_ADD(DATE_HIRE, INTERVAL 1 DAY), DATE_HIRE + 1

FROM EMPLOYEE_PAY_TBL; # 日期加1天,DATE_HIRE+1则会把日期转换为整数后再加1(mysql语法)

12.2.4 其他日期函数

日期函数         用途

DAYNAME(date)    显示星期几

DAYOFMONTH(date) 显示几日

DAYOFWEEK(date)  显示星期几

DAYOFYEAR(date)  显示一年中的第几天

12.3.1 日期描述

语法          日期元素

SECOND        秒

MINUTE        分钟

HOUR          小时

DAY           天

MONTH         月

YEAR          年

MINUTE_SECOND 分和秒

HOUR_MINUTE   小时和分

DAY_HOUR      天和小时

YEAR_MONTH    年和月

HOUR_SECOND   小时、分和秒

DAY_MINUTE    天和分钟

DAY_SECOND    天和秒

SELECT EXTRACT(YEAR FROM DATE_HIRE)

FROM EMPLOYEE_PAY_TBL; # 提取DATE_HIRE中的年

12.3.2 日期转换为字符串

SELECT DATE_HIRE, TO_CHAR(DATE_HIRE, 'Month dd, yyyy') HIRE

FROM EMPLOYEE_PAY_TBL; # 将日期转换为字符串(Oracle语法)

12.3.3 字符串转换为日期

SELECT STR_TO_DATE('01/01/2010 12:00:00 AM', '%m/%d/%Y %h:%i:%s %p') AS FORMAT_DATE

FROM EMPLOYEE_PAT_TBL; # 将字符串转换为日期格式(mysql语法)

第四部分  建立复杂的数据库查询

第13章  在查询里结合表

13.2.2 等值结合

SELECT EMPLOYEE_TBL.EMP_ID,

       EMPLOYEE_TBL.LAST_NAME,

       EMPLOYEE_PAY_TBL.POSITION

FROM EMPLOYEE_TBL,

     EMPLOYEE_PAY_TBL

WHERE EMPLOYEE_TBL.EMP_ID = EMPLOYEE_PAY_TBL.EMP_ID; # 等值结合

SELECT EMPLOYEE_TBL.EMP_ID,

       EMPLOYEE_PAY_TBL.DATE_HIRE

FROM EMPLOYEE_TBL INNER JOIN EMPLOYEE_PAY_TBL

ON EMPLOYEE_TBL.EMP_ID = EMPLOYEE_PAY_TBL.EMP_ID; # INNER JOIN ON语法与WHERE不同,但结果相同

13.2.3 使用表的别名

SELECT E.EMP_ID, EP.SALARY, EP.DATE_HIRE, E.LAST_NAME

FROM EMPLOYEE_TBL E,

     EMPLOYEE_PAY_TBL EP

WHERE E.EMP_ID = EP.EMP_ID

      AND EP.SALARY > 20000; # EMPLOYEE_TBL被重命名为E, EMPLOYEE_PAY_TBL被重命名为EP,别名能减少输入,减少输入错误

13.2.4 不等值结合

SELECT E.EMP_ID, E.LAST_NAME, P.POSITION

FROM EMPLOYEE_TBL E,

     EMPLOYEE_PAT_TBL EP

WEHRE E.EMP_ID <> P.EMP_ID;# 每个表6行记录,不等值结合返回30行记录

13.2.5 外部结合

SELECT P.PROD_DESC, O.QTY

FROM PRODUCTS_TBL P,

     ORDERS_TBL O

WHERE P.PROD_ID = O.PROD_ID(+); # 外部结合,显示P.PROD_DESC字段的全部记录,不管O.QTY字段是否为空

SELECT P.PROD_DESC, Q.QTY

FROM PRODUCTS_TBL P LEFT OUTER JOIN ORDERS_TBL O

ON P.PROD_ID = O.PROD_ID; # LEFT OUTER JOIN 左外部结合,语法同+不同,但结果相同

13.2.6 自结合

SELECT E1.NAME, E2.NAME

FROM EMP E1, EMP E2

WHERE E1.MGR_ID = E2.ID; # 自结合,将一个表等同两个不同的表进行比较

13.2.7 结合多个主键

SELECT P.PRODUCT_NAME, O.ORD_DATE, O.QUANTITY

FROM PROD P, ORD O

WHERE P.SERIAL_NUMBER = O.SERIAL_NUMBER

AND P.VENDOR_NUMBER = O.VENDOR_NUMBER; # 结合主键SERIAL_NUMBER和VENDOR_NUMBER

或者使用INNER JOIN ON

SELECT P.PRODUCT_NAME, O.ORD_DATE, O.QUANTITY

FROM PROD P, INNER JOIN ORD O

ON P.SERIAL_NUMBER = O.SERIAL_NUMBER

AND P.VENDOR_NUMBER = O.VENDOR_NUMBER;

13.3.1 使用基表

基表:如果需要从两个表里获取数据,但它们又没有公用字段,我们就必须结合另一个表,这个表与前两个表都有公用字段,这个表就被称为基表

SELECT C.CUST_NAME, P.PROD_DESC

FROM CUSTOMER_TBL C,

     PRODUCTS_TBL P,

     ORDERS_TBL O

WHERE C.CUST_ID = O.CUST_ID

  AND P.PROD_ID = O.PROD_ID; # ORDERS_TBL为基表

13.3.2 笛卡尔积

笛卡尔积是笛卡尔结合或“无结合”的结果,如果从两个或多个没有结合的表里获取数据,输出结果就是所有被选表里的全部记录,笛卡尔积通常也被称为交叉结合

SELECT E.EMP_ID, E.LAST_NAME, P.POSITION

FROM EMPLOYEE_TBL E,

     EMPLOYEE_PAY_TBL P; # 可怕的笛卡尔积

第14章  使用子查询定义未确定数据

14.1 什么是子查询

子查询也被称为嵌套查询,是位于另一个查询的WHERE子句里的查询,它返回的数据通常在主查询里作为一个条件,从而进一步限制数据库返回的数据

SELECT E.EMP_ID, E.LAST_NAME, E.FIRST_NAME, EP.PAY_RATE

FROM EMPLOYEE_TBL E, EMPLOYEE_PAY_TBL TP

WHERE E.EMP_ID = EP.EMP_ID

AND EP.PAY_RATE < (SELECT PAY_RATE

                   FROM EMPLOYEE_PAY_TBL

                   WHERE EMP_ID = '443679012'); # 子查询

14.1.2 子查询与INSERT语句

INSERT INTO RICH_EMPLOYEES

SELECT E.EMP_ID, E.LAST_NAME, E.FIRST_NAME, EP.PAY_RATE

FROM EMPLOYEE_TBL E, EMPLOYEE_PAY_TBL EP

WHERE E.EMP_ID = EP.EMP_ID

  AND EP.PAY_RATE > (SELECT PAY_RATE

                     FROM EMPLOYEE_PAY_TBL

                     WHERE EMP_ID = '220984332'); # 在INSERT语句中使用子查询

14.1.3 子查询与UPDATE语句

UPDATE FROM EMPLOYEE_PAY_TBL

SET PAY_RATE = PAY_RATE * 1.1

WHERE EMP_ID IN (SELECT EMP_ID

                 FROM EMPLOYEE_TBL

                 WHERE CITY = 'INDIANAPOLIS'); # UPDATE语句中使用子查询

14.1.4 子查询与DELETE语句

DELETE FROM EMPLOYEE_PAY_TBL

WHERE EMP_ID = (SELECT EMP_ID

                FROM EMPLOYEE_TBL

                WHERE LAST_NAME = 'GLASS'

                AND FIRST_NAME = 'BRANDON'); # DELETE语句中使用子查询

14.2 嵌套的子查询

SELECT CUST_ID, CUST_NAME

FROM CUSTOMER_TBL

WHERE CUST_ID IN (SELECT O.CUST_ID

                  FROM ORDERS_TBL O, PRODUCTS_TBL P

                  WHERE O.PROD_ID = P.PROD_ID

                    AND O.QTY * P.COST > (SELECT SUM(COST)

                                          FROM PRODUCTS_TBL)); # 嵌套的子查询、

14.3 关联子查询

关联子查询是依赖主查询里的信息的子查询

SELECT C.CUST_NAME

FROM CUSTOMER_TBL C

WHERE 10 < (SELECT SUM(O.QTY)

            FROM ORDERS_TBL O

            WHERE O.CUST_ID = C.CUST_ID); # 关联子查询

第15章  组合多个查询

15.1 单查询与组合查询

单查询是一个SELECT语句,而组合查询具有两个或多个SELECT语句

SELECT EMP_ID, SALARY

FROM EMPLOYEE_PAY_TBL

WHERE SALARY IS NOT NULL

UNION

SELECT EMP_ID, PAY_RATE

FROM EMPLOYEE_PAY_TBL

WHERE PAY_RATE IS NOT NULL; # 使用操作符UNION结合两个查询

15.2.1 UNION

UNION操作符可以组合两个或多个SELECT语句的结果,不包含重复的记录,列标题来自于第一个查询的字段名称

SELECT PROD_DESC FROM PRODUCTS_TBL

UNION

SELECT LAST_NAME FROM EMPLOYEE_TBL; # 使用UNION操作符组合两个不相关的查询

15.2.2 UNION ALL

UNION ALL操作符可以组合两个SELECT语句的结果,并且包含重复的结果

SELECT PROD_DESC FROM PRODUCTS_TBL

UNION ALL

SELECT PROD_DESC FROM PRODUCTS_TMP;

15.2.3 INTERSECT

INTERSECT可以组合两个SELECT语句,但只返回第一个SELECT语句里与第二个SELECT语句里一样的记录

SELECT CUST_ID FROM CUSTOMER_TBL

INTERSECT

SELECT CUST_ID FROM ORDERS_TBL; # 返回具有订单的顾客的ID

15.2.4 EXCEPT

EXCEPT操作符组合两个SELECT语句,返回第一个SELECT语句里有但第二个SELECT语句里没有的记录

SELECT PROD_DESC FROM PRODUCTS_TBL

EXCEPT

SELECT PROD_DESC FROM PRODUCTS_TMP;

15.3 组合查询里使用ORDER BY

SELECT EMP_ID FROM EMPLOYEE_TBL

UNION

SELECT EMP_ID FROM EMPLOYEE_PAY_TBL

ORDER BY 1; # ORDER BY用于组合查询时只能对全部查询结果排序,且组合查询里只能有一个ORDER BY子句,而且它只能以别名或数字来引用字段

15.4 组合查询里使用GROUP BY

SELECT 'CUSTOMERS' TYPE, COUNT(*)

FROM CUSTOMER_TBL

UNION

SELECT 'EMPLOYEES' TYPE, COUNT(*)

FROM EMPLOYEE_TBL

UNION

SELECT 'PRODUCTS' TYPE, COUNT(*)

FROM PRODUCTS_TBL

GROUP BY 1; # 利用一个字符串代表顾客记录、雇员记录、产品记录,GROUP BY子句用于把整个结果根据第一个字段进行分组

第五部分  SQL性能调整

第16章  利用索引改善性能

16.1 什么是索引

简单来说,索引就是一个指针,指向表里的数据

16.4.1 单字段索引

CREATE INDEX NAME_IDX

ON EMPLOYEE_TBL (LAST_NAME); # 对表EMPLOYEE_TBL中LAST_NAME字段创建单字段索引

16.4.2 唯一索引

唯一索引不允许表里具有重复值,除此之外,它与普通索引的功能一样

CREATE UNIQUE INDEX NAME_IDX

ON EMPLOYEE_TBL (LAST_NAME); # 对表EMPLOYEE_TBL中LAST_NAME字段创建唯一索引

16.4.3 组合索引

组合索引是基于一个表里两个或多个字段单索引

CREATE INDEX ORD_IDX

ON ORDERS_TBL (CUST_ID, PROD_ID); # 基于表ORDERS_TBL中的两个字段CUST_ID和PROD_ID创建组合索引

16.4.4 隐含索引

隐含索引是数据库服务程序在创建对象时自动创建的,比如,数据库会为主键约束和唯一性约束自动创建索引

16.5 何时考虑使用索引

外键经常用于与父表的结合,所以适合设置索引,一般来说,大多数用于表结合的字段都应该设置索引

经常在ORDER BY和GROUP BY里引用的字段也应该考虑设置索引

具有大量唯一值的字段,或是在WHERE子句里会返回很小部分记录的字段,都可以考虑设置索引

16.6 何时应该避免使用索引

索引不应该用于小规模的表

当字段用于WHERE子句作为过滤器会返回表里大部分记录时,该字段就不适合设置索引

经常会被批量更新单表可以具有索引,但批量操作的性能会由于索引而降低

不应该对包含大量NULL值的字段设置索引

经常被操作的字段不应该设置索引,因为对索引的维护会变得很繁重

16.7 修改索引

ALTER INDEX INDEX_NAME

16.8 删除索引

DROP INDEX INDEX_NAME ON TABLE_NAME # mysql语法

第17章  改善数据库性能

17.1 什么是SQL语句调整

SQL语句调整主要涉及调整语句的FROM和WHERE子句,因为数据库服务程序主要根据这两个子句执行查询

17.2 数据库调整与SQL语句调整

数据库调整是调整实际数据库的过程,包括分配内存、磁盘、CPU、I/O和底层数据库进程,还涉及数据库结构本身的管理与操作,比如表和索引的设计与布局

17.3.1 为提高可读性格式化SQL语句

每个子句都以新行开始

当子句里的参数超过一行长度需要换行时,利用制表符或空格来形成缩进

以一致的方式使用制表符和空格

当语句里使用多个表时,使用表的别名

如果SQL实现里允许使用注释,应该在语句里有节制地使用

如果在SELECT语句里要使用多个字段,就让每个字段都从新行开始

如果在FROM子句里要使用多个表,就让每个表名都从新行开始

让WHERE子句里每个条件都以新行开始

17.3.2 FROM子句里的表

把较小的表列在前面,把较大的表列在后面,就会获得更好的性能

17.3.3 结合条件的次序

在WHERE子句里,来自基表的字段一般放在结合操作的右侧,要被结合的表通常按照从小到大的次序排列

如果没有基表,那表就应该从小到大排列,让最大的表位于WHERE子句里结合操作的右侧

结合条件应该位于WHERE子句的最前面,其后才是过滤条件

17.3.4 最严格条件

最严格条件是WHERE子句里返回最少记录的条件

应该让SQL优化器首先计算最严格条件,因为它会返回最小但数据子集,从而减小查询的开销。最严格条件的位置取决于优化器的工作方式,若优化器从WHERE子句的底部开始读取,则需要把最严格条件放到WHERE子句的末尾

17.4 全表扫描

在没有使用索引时,或是SQL语句所使用的表没有索引时,就会发生全表扫描

下面是应该被索引的数据:

作为主键的字段

作为外键的字段

在结合表里经常使用的字段

经常在查询里作为条件的字段

大部分值是唯一值的字段

17.5.1 使用LIKE操作符和通配符

17.5.2 避免使用OR操作符

在SQL语句里用谓词IN代替OR操作符能够提高数据检索速度

17.5.3 避免使用HAVING子句

HAVING子句会让SQL优化器进行额外的工作,也就需要额外的时间,在可能的情况下,尽量不要在SQL语句中使用HAVING子句

17.5.4 避免大规模排序操作

大规模排序操作意味着使用ORDER BY、GROUP BY、HAVING子句,由于大规模排序操作不是总可以避免的,所以最好把大规模排序在批处理过程里,在数据库使用的非繁忙期运行,从而避免影响大多数用户进程的性能

17.5.5 使用存储过程

所谓存储过程就是经过编译的、以可执行格式永久保存在数据库里的SQL语句

17.5.6 在批加载时关闭索引

批加载可能包含数百、数千或数百万操作语句或事务,最好在批加载前删除索引,批加载结束后再重建索引

17.6 基于成本的优化

资源消耗量:

总计资源消耗 = 衡量方法 * 执行次数

第六部分  使用SQL管理用户和安全

第18章  管理数据库用户

18.2.1 创建用户

CREATE USER user [IDENTIFIED BY [PASSWORD] 'password'] # mysql语法,创建用户

GRANT priv_type [(column_list)] [, priv_type [(column_list)]]...

ON [object_type] {tbl_name | * | *.* | db_name.* | db_name.routine_name}

TO user # mysql语法,分配用户权限

CREATE USER JOHN; # 创建JOHN用户

GRANT SELECT ON TABLE EMPLOYEE_TBL TO JOHN; # 分配权限

DROP USER JOHN CASCADE; # 删除用户

18.2.2 创建规划

CREATE SCHEMA [SCHEMA_NAME] [USER_ID]

              [DEFAULT CHARACTER SET CHARACTER_SET]

              [PATH SCHEMA NAME [, SCHEMA NAME]]

              [SCHEMA_ELEMENT_LIST] # 创建规划,mysql不支持CREATE SCHEMA,在mysql中规划被看作一个数据库,可以用CREATE DATABASE来创建规划

18.2.3 删除规划

DROP SCHEMA SCHEMA_NAME { RESTRICT | CASCADE } # 删除规划,若规划里有对象,则必须指定CASCADE选项

18.2.4 调整用户

UPDATE mysql.user SET password=PASSWORD('new password')

WHERE user='username'; # mysql中重置用户密码

RENAME USER old_username TO new_username; # mysql中改变用户名

18.2.5 用户会话

CONNECT TO DEFAULT | STRING1 [AS STRING2] [USER STRING3]

DISCONNECT DEFAULT | CURRENT | ALL | STRING

SET CONNECTION DEFAULT | STRING # 连接和断开数据库,从而开始和结束SQL会话

18.2.6 禁止用户访问

DROP USER USER_ID [CASCADE] # 删除数据库里的用户

REVOKE PRIV1 [, PRIV2, ...] FROM USERNAME # 取消已经分配给用户的权限

第19章  管理数据库安全

19.3.1 GRANT命令

GRANT SELECT ON EMPLOYEE_TBL TO USER1; # 向用户授予权限

GRANT SELECT ON EMPLOYEE_TBL TO USER1 WITH GRANT OPTION; # 当对象所有者利用GRANT OPTION把自己对象的权限授予另一个用户时,这个用户还可以把这个对象的权限授予其他用户

GRANT CREATE TABLE TO USER1 WITH ADMIN OPTION; # 当一个用户用ADMIN OPTION向另一个用户授予系统权限之后,后者还可以把系统权限授予其他用户

19.3.2 REVOKE命令

REVOKE INSERT ON EMPLOYEE_TBL FROM USER1; # 撤销权限,RESTRICT选项只有当REVOKE命令里指定的权限撤销之后不会导致其他用户产生报废权限时,REVOKE才能顺利完成,而CASCADE选项会撤销权限,不会遗留其他用户的权限

19.3.3 控制对单独字段的访问

GRANT UPDATE (NAME) ON EMPLOYEES TO PUBLIC; # 分配表里指定字段的权限

19.3.4 数据库账户PUBLIC

GRANT SELECT ON EMPLOYEE_TBL TO PUBLIC; # 数据库账户PUBLIC是个代表数据库里全体用户的账户,所有用户都属于PUBLIC账户

19.3.5 权限组

19.4 通过角色控制权限

19.4.1 CREATE ROLE语句

CREATE ROLE RECORDS_CLERK; # 创建角色

GRANT SELECT, INSERT, UPDATE, DELETE ON EMPLOYEE_PAY TO RECORDS_CLERK; # 授予角色权限

GRANT RECORDS_CLERK TO USER1; # 授予用户角色

19.4.2 DROP ROLE语句

DROP ROLE RECORDS_CLERK; # 删除角色

19.4.3 SET ROLE语句

SET ROLE RECORDS_CLERK; # 为用户的SQL会话设置角色

第七部分  摘要数据结构

第20章  创建和使用视图及异名

20.1 什么是视图

视图是一个虚拟表,视图可以包含表的全部或部分记录,可以由一个或多个表创建,表与视图的主要区别在于表包含实际的数据、占据物理存储空间,而视图不包含数据,而且只需要保存视图定义(即查询语句)。

20.2.1 从一个表创建视图

CREATE VIEW EMP_VIEW AS

SELECT LAST_NAME, FIRST_NAME, MIDDLE_NAME

FROM EMPLOYEE_TBL; # 选择指定的字段创建视图

20.2.2 从多个表创建视图

CREATE VIEW EMPLOYEE_SUMMARY AS

SELECT E.EMP_ID, E.LAST_NAME, P.POSITION, P.DATE_HIRE, P.PAY_RATE

FROM EMPLOYEE_TBL E,

     EMPLOYEE_PAY_TBL P

WHERE E.EMP_ID = P.EMP_ID; # 从多个表创建视图

20.2.3 从视图创建视图

CREATE VIEW2 AS

SELECT * FROM VIEW1; # 从VIEW1创建VIEW2

20.3 WITH CHECK OPTION

CREATE VIEW EMPLOYEE_PAGERS AS

SELECT LAST_NAME, FIRST_NAME, PAGER

FROM EMPLOYEE_TBL

WHERE PAGER IS NOT NULL

WITH CHECK OPTION; # WITH CHECK OPTION会确保视图的PAGER字段里不包含NULL值

20.4 从视图创建表

CREATE TABLE CUSTOMER_ROSTER_TBL AS

SELECT CUST_ID, CUST_NAME

FROM ACTIVE_CUSTOMERS; # 从视图ACTIVE_CUSTOMERS创建表CUSTOMER_ROSTER_TBL

20.5 视图与GROUP BY子句

CREATE VIEW NAMES2 AS

SELECT LAST_NAME || ', ' || FIRST_NAME || ' ' ||MIDDLE_NAME NAME

FROM EMPLOYEE_TBL

GROUP BY LAST_NAME || ', ' || FIRST_NAME || ' ' || MIDDLE_NAME; # CREATE VIEW语句里不能包含ORDER BY子句,但是GROUP BY子句用于CREATE VIEW语句时,可以起到类似ORDER BY子句的作用

20.7 删除视图

DROP VIEW NAMES2; # 删除视图,若使用RESTRICT选项,当其他视图在约束里有所引用,删除操作就会出错,若使用CASCADE选项,则底层视图或约束也会被删除

20.9 什么是异名

异名就是表或视图的另一个名称,mysql不支持异名,但可以使用视图来实现同样的功能

20.9.1 创建异名

CREATE SYNONYM CUST FOR CUSTOMER_TBL; # 创建CUSTOMER_TBL的异名CUST

20.9.2 删除异名

DROP SYNONYM CUST; # 删除异名CUST

第21章  使用系统目录

21.1 系统目录

系统目录是一些表和视图的集合,它们包含了关于数据库的信息,在mysql里系统目录位于mysql数据库里

第八部分  在实际工作中应用SQL知识

第22章  高级SQL主题

22.1 光标

光标被用于通过以记录为单位的操作,来获得数据库中数据的子集

DECLARE CURSOR_NAME CURSOR

FOR SELECT_STATEMENT; # mysql里对光标的声明语法

22.1.1 打开光标

OPEN CURSOR_NAME; # mysql里打开光标语法

22.1.2 从光标获取数据

FETCH CURSOR_NAME INTO VARIABLE_NAME, [VARIABLE_NAME] ... # mysql里从光标获取数据放到变量中

mysql光标处理过程:

BEGIN

     DECLARE done INT DEFAULT 0;

     DECLARE custname VARCHAR(30);

     DECLARE namecursor CURSOR FOR SELECT CUST_NAME FROM TBL_CUSTOMER;

     OPEN namecursor;

     read_loop: LOOP

         FETCH namecursor INTO custname;

         IF done THEN

             LEAVE read_loop;

         END IF;

         -- Do something with the variable

     END LOOP;

     CLOSE namecursor;

END;

22.1.3 关闭光标

CLOSE CURSOR_NAME; # mysql中关闭光标语法

22.2 存储过程和函数

存储过程是保存在数据库里的一组SQL语句或函数,它们被编译,随时可以被数据库用户使用

mysql创建存储过程的语法:

CREATE [OR REPLACE] PROCEDURE PROCEDURE_NAME

[(ARGUMENT [{IN | OUT | IN OUT}] TYPE,

ARGUMENT [{IN | OUT | IN OUT}] TYPE)] {AS}

PROCEDURE_BODY

例如:

CREATE PROCEDURE NEW_PRODUCT

(PROD_ID IN VARCHAR2, PROD_DESC IN VARCHAR2, COST IN NUMBER)

AS

BEGIN

    INSERT INTO PRODUCTS_TBL

    VALUES (PROD_ID, PROD_DESC, COST);

    COMMIT;

END; # 存储过程,在表PRODUCTS_TBL里插入一行新记录

CALL NEW_PRODUCT('9999', 'INDIAN CORN', 1.99); # 执行上面创建的过程

22.3 触发器

触发器是存储过程的一种,在特定DML行为作用于表格时被执行

22.3.1 CREATE TRIGGER语句

CREATE [DEFINER={user | CURRENT_USER}]

TRIGGER TRIGGER_NAME

{BEFORE | AFTER}

{INSERT | UPDATE | DELETE [, ..]}

ON TABLE_NAME

AS

SQL_STATEMENTS

22.3.2 DROP TRIGGER语句

DROP TRIGGER TRIGGER_NAME; # 删除触发器

22.3.3 FOR EACH ROW语句

CREATE TRIGGER TRIGGER_NAME

ON TABLE_NAME

FOR EACH ROW

SQL_STATEMENT; # FOR EACH ROW在SQL语句影响每条记录时都触发

22.7 直接SQL与嵌入SQL

{HOST PROGRAMMING COMMANDS}

EXEC SQL {SQL STATEMENT};

{MORE HOST PROGRAMMING COMMANDS} # 在主机程序(比如ANSI C)里嵌入SQL

22.9 使用XML

EXTRACTVALUE([XML Fragment], [locator string]) # 从XML文档或片段里获取信息

例如:

SELECT EXTRACTVALUE('<a>Red</a><b>Blue</b>', '/a') AS ColorValue; # 从节点a里提取值

第23章  SQL扩展到企业、互联网和内部网

第24章  标准SQL的扩展
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  sql