数据库
首页 > 数据库> > SQL学习&实战笔记

SQL学习&实战笔记

作者:互联网

前言

分享SQL学习过程,归纳重点,记录其在数据仓库(ETL)开发中的应用及本人所踩过的坑。

一、MySQL

(一)SQL必知必会

1. 了解SQL

(1)数据库(database), 数据库管理系统(DBMS)
(2)表(table),mysql中,表的含义是存储数据的结构化文件。
(3)列(column),列表示表中的一个字段。
(4)行(row),行表示表中的一条记录。
(5)主键(primary key),主键是一个字段或多个字段的组合(联合主键),用来唯一标识表中的一条记录。
(6)SQL语言分类,数据查询语言(DQL,如SELECT,FROM,WHERE);数据操纵语言(DML,如INSERT,UPDATE,DELETE);数据定义语言(DDL,如CREATE TABLE);数据控制语言(DCL,如GRANT)。

2. 检索数据

(1)DISTINCT,指示查询只返回不同的值。
注意点1:不能部分使用DISTINCT,DISTINCT关键字作用于所有的列,不仅仅是跟在其后面的那一列。例如,你指定如下查询,因为指定的两列不完全相同,所以所有列都会被检索出来。

SELECT DISTINCT
    vend_id
   ,prod_price
FROM Products;

注意点2:DISTINCT因为限制较多(不能和聚合函数并列使用)且性能较差,在数据仓库中Hive SQL通常不用DISTINCT,而用GROUP BY去重

(2)LIMIT指定返回的行数,LIMIT带的OFFSET指定从哪里开始。
注意点1:第0行,第一个被检索的行是第0行,而不是第1行。因此如下查询会检索第2行,而不是第1行。

SELECT
    prod_name
FROM Product
LIMIT 1 OFFSET 1;

3. 排序检索数据

(1)ORDER BY,默认是ASC升序排列,降序需要指定DESC关键字。
注意点1:如果想在多个列上进行降序排序,必须对每一列指定DESC关键字。

SELECT
    prod_id
   ,prod_price
   ,prod_name
FROM Products
# ORDER BY prod_price;
ORDER BY prod_price DESC, prod_name DESC;

4. 过滤数据

(1)WHERE子句操作符

操作符中文含义
=等于
<> / !=不等于
<小于
<=小于等于
!<不小于
>大于
>=大于等于
!>不大于
BETWEEN在两个指定值之间
NULL为NULL值

(2)NULL,过滤字段为NULL时用IS,不用等于号。

WHERE prod_price IS NULL

5. 高级数据过滤

(1)AND在求值过程中优先级更高,最好使用小括号对操作符进行明确分组,避免与OR等连用时执行顺序错误。

SELECT
    prod_name
   ,prod_price
FROM Products
WHERE (vend_id = 'DLL01' OR vend_id = 'BRS01') AND prod_price >= 10;
# 不加小括号会先执行 vend_id='BRS01' AND prod_price >= 10

(2)IN操作符,在WHERE子句中用来指定要匹配值得清单,功能与OR相当,但执行更快。

SELECT
    prod_name
   ,prod_price
FROM Products
WHERE vend_id IN ('DLL01', 'BRS01');
# WHERE (vend_id='DLL01' OR vend_id='BRS01')

6. 用通配符进行过滤(耗时长,需考虑性能问题)

(1)%匹配多个字符

SELECT
    prod_name
FROM Products
WHERE prod_name LIKE 'Fish%';

输出
prod_name
------------------------
Fish bean bag toy

(2)_匹配单个字符

SELECT
    prod_name
FROM Products
WHERE prod_name LIKE '__ inch teddy bear';

输出
prod_name
------------------------
12 inch teddy bear
18 inch teddy bear

(3)[]匹配指定位置的1个字符,如下匹配[JM]中J或者M

SELECT
    cust_contact
FROM Customers
WHERE cust_contact LIKE '[JM]%'
ORDER BY cust_contact;

输出
cust_contact
------------------------
Jim Jones
John Smith
Michelle Green

7. 创建计算字段

(1)常用操作符

操作符说明
+
-
*
/

(2)执行算术计算,汇总订单物品总价。

SELECT
    prod_id
   ,quantity
   ,item_price
   ,quantity * item_price    AS expanded_price
FROM OrderItems
WHERE order_num = 20008;

8. 使用函数处理数据

(1)常用文本处理函数

函数说明
LEFT() (或使用子字符串函数)返回字符串左边的字符
RIGHT()(或使用子字符串函数)返回字符串右边的字符
LENGTH()(也使用DATALENGTH()或LEN())返回字符串长度
LOWER()(Access使用LCASE())将字符串转为小写
UPPER()(Access使用UCASE())将字符串转为大写
LTRIM()去左边空格(比如:字段首)
RTRIM()去右边空格(比如:字段末)

(2)常用数值处理函数

函数说明
ABS()返回一个数的绝对值
PI()返回圆周率
SQRT()返回平方根

9. 汇总数据

(1)SQL聚集函数

函数说明
AVG()返回某列的平均值
COUNT()返回某列的行数
MAX()返回某列的最大值
MIN()返回某列的最小值
SUM()返回某列之和

注意点1:AVG(),MAX(),MIN()和SUM()函数会忽略列值为NULL的行。
注意点2:使用COUNT(1)和COUNT(*)对表中的数据进行计算,不管表中列包含的是空值(NULL)还是非空值,都会计算进来。使用COUNT(column)进行计算时,会忽略NULL值。

# COUNT(*)
SELECT
    COUNT(*)    AS num_cust
FROM Customers;

输出
num_cust
------------------------
5

# COUNT(column)
SELECT
    COUNT(cust_email)    AS num_cust
FROM Customers;

输出
num_cust
------------------------
3

10. 分组数据

(1)HAVING和WHERE的区别。WHERE在数据分组前进行过滤,HAVING在数据分组后进行过滤。WHERE排除的行不包括在分组中,这可能会改变计算值,从而影响HAVING子句中基于这些值过滤掉的分组。

SELECT
    vend_id
   ,COUNT(*)    AS num_prods
FROM Products
WHERE prod_price >= 4
GROUP BY vend_id
HAVING COUNT(*) >= 2;

(2)SELECT子句顺序,在写SELECT语句时必须遵循如下次序。

子句说明
SELECT要返回的列或表达式
FROM从中检索数据的表
WHERE行级过滤
GROUP BY分组说明
HAVING组级过滤
ORDER BY输出排序顺序

11. 使用子查询(需根据实际情况采用,使用联结通常性能更好)

(1)如下,把第一个查询变为子查询。

# 第一个查询
SELECT
    order_num
FROM OrderItems
WHERE prod_id = 'RGAN01';

输出
order_num
------------------------
20007
20008

# 第二个查询
SELECT
    cust_id
FROM Orders
WHERE order_num IN (20007, 20008);

输出
cust_id
------------------------
1000000004
1000000005

# 把第一个查询变为子查询
SELECT
    cust_id
FROM Order_num IN
(
    SELECT
        order_num
    FROM OrderItems
    WHERE prod_id = 'RGAN01'
);

输出
cust_id
------------------------
1000000004
1000000005

12. 联结表

(1)WHERE关联

SELECT
    vend_name
   ,prod_name
   ,prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;

(2)笛卡尔积,由没有联结条件的表关系返回的结果为笛卡尔积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。

SELECT
    vend_name
   ,prod_name
   ,prod_price
FROM Vendors, Products;

(3)内联结(取交集)

SELECT
    vend_name
   ,prod_name
   ,prod_price
FROM Vendors
INNER JOIN Products
ON Vendors.vend_id = Products.vend_id;

13. 创建高级联结

(1)外联结 LEFT OUTER JOIN,左表所有行匹配右表。
(2)全联结 FULL OUTER JOIN,取全部,包含不关联的行。

14. 组合查询

(1)UNION和UNION ALL,UNION会自动去除重复的行,UNION ALL不去除重复的行。

15. 插入数据

(1)插入检索出的数据

INSERT INTO Customers (cust_id, cust_name, cust_address)
VALUES ('1006', 'Toy Land', '123 Any Street');

(2)从一个表复制到另一个表

CREATE TABLE CustCopy AS
SELECT
*
FROM Customers;

16. 更新删除数据

(1)更新数据,SET命令用来将新值赋给被更新的列。

UPDATE Customers
SET cust_email = 'kim@thetoystore.com'
WHERE cust_id = '1005';

(2)删除数据,如下操作为删除cust_id = '1006’的行。DELETE不删除表结构,如果需要删除表中的所有行,使用TRUNCATE TABLE速度更快(因为不记录数据的变动)。

DELETE FROM Customers
WHERE cust_id = '1006';

TRUNCATE TABLE Customers;

(3)删除表

# 删除数据同时删除表结构
DROP TABLE Customers;

17. 创建和操纵表

(1)创建表

CREATE TABLE Products
(
    prod_id       CHAR(10)         NOT NULL
   ,vend_id       CHAR(10)         NOT NULL
   ,prod_name     CHAR(254)        NOT NULL
   ,prod_price    DECIMAL(8, 2)    NOT NULL
   ,prod_desc     VARCHAR(1000)    NULL
);

(2)更新表

ALTER TABLE Vendors ADD vend_phone CHAR(20);
ALTER TABLE Vendors DROP COLUMN vend_phone;

18. 使用视图

(1)原理,视图是虚拟的表,与包含数据的表不一样,视图只包含使用时动态检索数据的查询。
(2)缺点,因为视图不包含数据,所以每次使用视图时,都必须处理查询执行需要的所有检索,如果用多个联结和过滤创建了复杂的视图或者嵌套了视图,性能可能下降得很厉害。
(3)作用,视图提供了一种封装SELECT语句的层次,可用来简化数据处理提高复用率,重新格式化或保护基础数据。

# 查询订购了产品'RCAN01'的顾客
SELECT
    cust_name
   ,cust_contact
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num;
AND prod_id = 'RCAN01';

# 把上述查询封装成视图(虚拟表)以便复用
CREATE VIEW ProductCustomers AS
SELECT
    cust_name
   ,cust_contact
   ,prod_id
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num;

# 利用视图查询订购了产品'RCAN01'的顾客
SELECT
    cust_name
   ,cust_contact
FROM ProductCustomers
WHERE prod_id = 'RCAN01';
# 增加一个'()'的格式效果
SELECT
    RTRIM(vend_name) + '(' + RTRIM(vend_country) + ')'    AS vend_title
FROM Vendors
ORDER BY vend_name;

输出
vend_title
------------------------
Bear Emporium (USA)
Bears R US (USA)
Fun and Games (England)

# 如果经常需要这个格式效果,可以创建视图来使用
CREATE VIEW VendorLocations AS
SELECT
    RTRIM(vend_name) + '(' + RTRIM(vend_country) + ')'    AS vend_title
FROM Vendors;

# 查询增加'()'的格式效果的视图
SELECT
    vend_title
FROM VendorLocations;

19. 使用存储过程

(1)存储过程就是为以后使用而保存的一条或多条SQL语句。可将其视为批文件,虽然其作用不仅限于批处理。
(2)执行存储过程

# 将一个新产品添加到Producs表中,如下存储过程有四个参数:供应商ID,产品名,价格和描述
EXECUTE AddNewProduct
(
    'JTS01'
   ,'Stuffed Eiffel Tower'
   ,6.49
   ,'Plush stuffed toy with the text La Tour Eiffel in red' 
);

(3)创建存储过程

# Oracle版本,对邮件发送清单中具有邮件地址的顾客进行计算
CREATE PROCEDURE MailingListCount
(
    ListCount OUT INTEGER
)
IS v_rows INTEGER;

BEGIN
    SELECT
        COUNT(*) INTO v_rows
    FROM Customers
    WHERE NOT cust_email IS NULL;
    ListCount := v_rows;
END;

# 调用
var ReturnValue NUMBER
EXEC MailingListCount(:ReturnValue);
SELECT ReturnValue;

20. 管理事务处理

(1)使用事务处理,通过确保成批的SQL操作要么完全执行,要么完全不执行,来维护数据库的完整性。

21. 使用游标

(1)游标(sursor)是一个存储在DBMS服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集。在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。
(2)创建游标

# 创建游标来检索没有电子邮件地址的所有顾客
DECLARE CustCursor CURSOR
FOR
SELECT
    cust_name
FROM Customers
WHERE cust_email IS NULL;

# Oracle版本
DECLARE CustCursor CURSOR
IS
SELECT
    cust_name
FROM Customers
WHERE cust_email IS NULL;

(3)使用游标

OPEN CURSOR CustCursor

# Oracle版本,从游标中检索第一行
DECLARE TYPE CustCursor IS REF CURSOR
    RETURN Customers%ROWTYPE;
DECLARE CustRecord Customers%ROWTYPE
BEGIN
    OPEN CustCursor;
    FETCH CustCursor INTO CustRecord;
    CLOSE CustCursor;
END;

22. 高级SQL特征

(1)主键,主键是一种特殊的约束,用来保证一列(或者一组列)中的值是唯一的,而且永不改动,主键不能为空且不能重复。如果没有主键,要安全地UPDATE或DELETE特定行而不影响其他行会非常困难。

# 建表时直接设置主键
CREATE TABLE Vendors
(
    vend_id         CHAR(10)    NOT NULL PRIMARY KEY
   ,vend_name       CHAR(10)    NOT NULL
   ,vend_address    CHAR(50)    NULL
   ,vend_city       CHAR(50)    NULL
   ,vend_state      CHAR(5)     NULL
   ,vend_zip        CHAR(10)    NULL
   ,vend_country    CHAR(50)    NULL
);

# 给现有表添加主键
ALTER TABLE Vendors
ADD CONSTRAINT PRIMARY KEY (vend_id);

(2)外键,外键是表中的一列,其值必须列在另一表的主键中,外键是保证引用完整性极其重要的部分。如下:通过Customers表中cust_id,关联Orders表中的cust_id,来获取顾客的订单情况。

# 建表时直接设置外键
CREATE TABLE Orders
(
    order_num    INTEGER    NOT NULL PRIMARY KEY
   ,order_data   DATETIME   NOT NULL
   ,cust_id      CHAR(10)   NOT NULL REFERENCES
);

# # 给现有表添加外键
ALTER TABLE Orders
ADD CONSTRAINT FOREIGN KEY (cust_id) REFERENCES Customers (cust_id);

(二)上述样例表表结构

1. Vendors表

说明
vend_id唯一供应商ID
vend_name供应商名
vend_address供应商地址
vend_city供应商所在城市
vend_state供应商所在州
vend_zip供应商地址邮编
vend_country供应商所在国家

2. Products表

说明
prod_id唯一产品ID
vend_id唯一供应商ID(关联到Vendor表的vend_id)
prod_name产品名
prod_price产品价格
prod_desc产品描述

3. Customers表

说明
cust_id唯一顾客ID
cust_name顾客名
cust_address顾客地址
cust_city顾客所在城市
cust_state顾客所在州
cust_zip顾客地址邮编
cust_country顾客所在国家
cust_contact顾客联系名
cust_email顾客电子邮件地址

4. Orders表

说明
order_num唯一订单号
order_data订单日期
cust_id唯一顾客ID(关联到Customers表的cust_id)

5. OrderItems表

说明
order_num唯一订单号(关联到Orders表的order_num)
order_item订单物品号(订单内的顺序)
prod_id产品ID(关联到Products表的Prod_id)
quantity物品数量
item_price物品价格

(三)数据类型

1. 字符串数据类型

(1)定长字符串和变长字符串,DBMS处理定长列远比处理变长列快得多
注意点1:字符串值都必须括在引号内。

数据类型说明
CHAR1~255个字符的定长字符串,长度必须在创建时规定
NCHARCHAR的特殊形式,用来支持多字节或Unicode字符
NVARCHARTEXT的特殊形式,用来支持多字节或Unicode字符
TEXT(也称LONG、MEMO或VARCHAR)变长文本

2. 数值数据类型

注意点1:数值不应该括在引号内。

数据类型说明
BIT单个二进制位值,或者为0或者为1,主要用于开/关标志
DECIMAL(或NUMRIC)定点或精度可变的浮点值
FLOAT(或NUMBER)浮点值
INT(或INTEGER)4字节整数值,支持-2147483648~2147483647的数
REAL4字节浮点值
SMALLINT2字节整数值,支持-32768~32767的数
TINYINT1字节整数值,支持0~255的数

3. 日期和时间数据类型

数据类型说明
DATE日期值
DATETIME(或TIMESTAMP)日期时间值
SMALLDATETIME日期时间值,精确到分(无秒或毫秒)
TIME时间值

4. 二进制数据类型

(1)二进制数据类型可包含任何数据,甚至包含二进制信息,如图像、多媒体、字处理文档等。

数据类型说明
BINARY(或VARBINARY)定长二进制数据(最大长度从255B到8000B)
LONG RAW变长二进制数据,最长2GB
RAW定长二进制数据,最多255B

(四)注意点整理

1. 引擎InnoDB和MyISAM的选择

(1)考虑是否要支持事务,如果要选择InnoDB,如果不需要可以考虑MyISAM。
(2)如果日常绝大多数都只是查询,可以考虑MyISAM,如果既有读写也挺频繁,使用InnoDB。
(3)系统奔溃后,MyISAM恢复起来更困难。
(4)MySQL5.5版本开始InnoDB已经成为默认引擎。

2. Hive2mysql时遇到的特殊字段类型

(1)字段长度过长时建议使用TEXT(同样会很占用空间),不用过长的VARCHAR(比如:VARCHAR(5000))
(2)含有错别字、特殊字符或者表情包类的字段可以使用BLOB字段类型(比如:用户评论字段的字段类型可以使用BLOB,防止出现表情包类的信息时,字段格式为VARCHAR会插入报错),BLOB最大字符约为65KB,TINYBLOB最大字符为255B。

3. mysql表结构设计

(1)设计mysql表结构的时候,应考虑后续查询的性能问题,字段长度设置的时候不应过长,够用就好,否则会占用过多空间,查询时增加IO负担。
(2)用作计算或者排序的字段应使用int等数值数据类型,不使用VARCHAR等字符串数据类型,避免计算或者排序时出错。
(3)小数类字段类型使用DECIMAL(比如:金额字段用DECIMAL(12, 2))

4. mysql新增字段

(1)当表数据量为千万级以下时,加多个字段用一条语句完成。因为当有复杂SQL运行时,加字段时会导致锁表,假设一条加字段语句执行需要20min,用n条语句加n个字段则执行时间变为20min * n,所以建议用一条语句完成。

二、HiveSQL

(一)常用函数

函数说明
SUM()求和
COUNT()求数量
SUBSTR()截取字符串
MAX()取最大
MIN()取最小
ROW_NUMBER()排序取最新值
LOWER()转小写
UPPER()转大写
RLIKE/NOT RLIKEB是否在A里(支持正则)
LIKE/NOT LIKEB是否在A里(不支持正则)
COALESCE(参数1, 参数2, 参数n)依次查各参数,遇到非NULL值即停止并返回该值
CASE WHEN当XX时返回XXX
FROM_UNIXTIME(UNIX_TIMESTAMP(xxx), ‘yyyy-mm-dd hh:mm:ss’)时间戳
TO_DATE()转yyyy-mm-dd的时间格式
DATE_FORMAT()转自定义时间格式
CONCAT(参数1, 参数2)拼接参数
REPLACE()替换
GET_JSON_OBJECT解析json

(二)注意点整理

注意点1:表JOIN前先过滤数据,减少JOIN的数据量。
注意点2:多次用到同一个查询,使用临时表以便复用。

三、参考书籍

(一)《SQL必知必会(第4版)》 【美】Ben Forta 著 钟鸣 刘晓霞 译
(二)《Hive编程指南》 【美】Edward Capriolo Dean Wampler Json Rutberglen 著 曹坤 译

标签:实战,cust,笔记,vend,SQL,prod,id,SELECT,name
来源: https://blog.csdn.net/zhangliushi/article/details/113466231