Jimmy Chen

A Programmer

(原创)SQL必知必会笔记

SQL必知必会笔记

SQL必知必会全书一共22章,涵盖了数据库增删改查的操作,但是可能是定义为入门书籍,所以书籍中的内容还是比较简单的,其中就用了14章来着重的介绍了查的部分,增删改都是指用一个或半个章节来介绍,不过对于刚刚开始学习数据库的新手来说,了解认识数据库也足够了,后续可以通过其他的书籍来了解数据库的其他高级特性。

  要了解数据库的操作,实践也是很重要的,书中附录A给出了样例脚本,大家可以到 http://www.forta.com/books/0672336073/TeachYourselfSQL_MySQL.zip 地址下载MySQL的样例,我学习使用的也是MySQL数据库。然后按照里面的readme进行操作就行了。

ps: 因为只是简单记录一下数据库的操作,所以会带有一些例子,但是不会具体给出例子执行后的答案,建议大家构建上面的数据库然后具体执行一下看看检索的结果,会更加的清晰。

1、SELECT操作

1.1 基本句式

  SELECT操作的基本句式都是SELECT FROM,即从哪个数据库表中查找什么数据,例如

SELECT * FROM Customers;

就表明从Customers表中查找所有的数据。当然不推荐一直使用SELECT *,因为查找所有的数据会导致数据库性能下降,耗费资源。如果要检索一个列,就在SELECT关键字后面加上需要检索的列就行了,如果要检索多个列,在SELECT后加上这几个列,列名与列名之间用逗号分隔开,例如:

SELECT prod_id, prod_name, prod_price FROM Products;

这样就能检索出prod_id、prod_name和prod_price三个列了。

  另外SQL语句是不区分大小写的,在上面的语句中,你使用全部大写、全部小写或者大小写混合,结果都是一样的。

1.2 DISTINCT关键字

DISTINCT关键字如它的意思一样,是用来合并查询过程中出现的相同列的。举例说,就是如果你只检索一列,然后这一列中有六条数据,分别是A,B,B,C,C,C,六条数据。如果你希望SELECT查询帮你将两条相同的B和三条相同的C合并为一条,可以通过DISTINCT关键字实现。DISTINCT关键字放在需要查询的列名前,例如:

SELECT DISTINCT vend_id FROM Products; 

从Products表中查询vend_id列,DISTINCT表明合并相同的列。

1.3 LIMIT && OFFSET关键字

LIMIT关键字

LIMIT关键表示限制SELECT返回的行数,例如LIMIT 5表示SELECT结果最多只能返回5行。

OFFSET关键字表示SELECT返回的数据从第几行开始输出显示,例如OFFSET 4表示和没有用OFFSET关键字限定的SELECT查询比,带OFFSET的查询会从第4行开始显示,前面的3行的数据就跳过了。

SELECT num FROM Alpha LIMIT 5;

ps: Alpha表示我自己添加用于记录的,里面只有一列num,内容就是大写的因为字母,A,B,C,D,E……

上面的例子中,如果没有LIMIT 5的话就会列出num列中的所有数据,但是加上LIMIT 5后就只会列出5行。

OFFSET关键字

SELECT num FROM Alpha LIMIT 2 OFFSET 3;

上面的例子中,如果没有LIMIT和OFFSET限制时,输出是A,B,C,D,E,F,G…..的话,那么LIMIT 2表明只输出两行,OFFSET 3跳过前面三行,所以这里输出的是D,E

这里要注意的是,OFFSET是不能单独使用的,需要和LIMIT一同使用。

1.4 数据排序

ORDER BY 关键字

SELECT检索出来的数据一般都是乱序的,没有特定的规律。但是我们平时检索的时候一般会有这样的需求,我们希望某列的数据按升序排序好分析趋势。排序用到的关键字是ORDER BY后面跟需要排序的列名。例如

SELECT num FROM Alpha ORDER BY num;

上面的SELECT语句检索出来后就会默认按照升序进行排列,如果想按照降序排列,那么可以在最后添加DESC关键字:

DESC 关键字

SELECT num FROM Alpha ORDER BY num DESC;

这样,上面的语句就会按照降序排列了,J、I、H…..

当然也可以按照多个列进行排序,只需要在ORDER BY后用逗号分隔多个列就好了。数据库会首先按照ORDER BY后第一顺位的列进行排序,如果该列存在相同的内容,在按照第二顺位的列进行排序,一次类推。例如:

多列排序

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

上面的例子将首先按照prod_price进行排序,如果prod_price内容相同,再按照prod_name排序。

按列文字排序

还有一种方式可以获得和上面一个例子一样的输出:

SELECT prod_id, prod_price, prod_name FROM Products ORDER BY 2,3;

可以看到,ORDER BY后面通过数字2,3来分别表示prod_price和prod_name。所以这条语句的输出是和前一条语句的输出是一致的。

1.5 过滤数据

WHERE关键字

数据的过滤需要用到WHERE关键字,WHERE关键字后面跟列名及其条件,例如:

SELECT num FROM Alpha WHERE num = 'A';

那么这条语句通过WHERE设定了num=’A’,那么就只会输出num=’A’的那一行,如果有多行就输出多行。当然除了‘=’操作外,还包含其他的操作,如下表格:

操作符 说明 操作符 说明
= 等于 > 大于
<> 不等于 >= 大于等于
!= 不等于 !> 不大于
< 小于 BETWEEN 指定的两个值之间
<= 小于等于 IS NULL 为NULL值
!< 不小于

上面的操作符和‘=’操作时类似的,大家多试试就会了,例如:

SELECT num FROM Alpha WHERE num > 'D';

上面的语句就输出E,F,G…..

组合WHERE子句

如果一个WHERE限定得出的数据还是过多的话,就可以通过组合WHERE子句来再一步限定输出,要使用WHERE子句的同时需要了解两个关键字AND和OR,在讲WHERE子句的过程中会同步讲解这两个关键字。

如果想同时让检索的数据符合两条WHERE要求的话,就需要使用AND关键字对这两个WHERE子句进行组合了:

SELECT prod_id, prod_price, prod_name FROM Products WHERE vend_id = 'DLL01' AND prod_price <= 4;

所以上面的语句检索出来的结果就是既符合vend_id='DLL01'又符合prod_price<=4这两个条件的。而OR关键字就刚好相反:

SELECT prod_id, prod_price, prod_name FROM Products WHERE vend_id = 'DLL01' OR prod_price <= 4;

上面的语句检索出来的结果是只要符合vend_id='DLL01'或者prod_price<=4其中之一个条件即可。

再多的WHERE子句也按照上面的内容组合即可。

AND和OR的求值顺序

只要记住这句话即可:SQL在处理OR操作符之前会优先处理AND操作符。

所以:

SELECT prod_name, prod_price FROM Products WHERE vend_id='DLL01' OR vend_id = 'BRS01' AND prod_price >= 10;

就等同于:

SELECT prod_name, prod_price FROM Products WHERE vend_id='DLL01' OR (vend_id = 'BRS01' AND prod_price >= 10);

所以这里推荐的是,如果既包含AND操作符和OR操作符,比较好的处理方式是按照逻辑添加圆括号()进行处理。这样阅读起来也比较流畅。

IN 操作符

除了使用WHERE组合子句,还可以通过IN操作符来限定检索条件:

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

上面的语句表示检索的数据只要符合vend_id='DLL01' 或者 vend_id='BRS01'其中之一个条件即可。是不是有点熟悉,其实这条语句是和下面一条语句起到相同的作用的:

SELECT prod_name, prod_price FROM Products WHERE vend_id = 'DLL01' OR vend_id = 'BRS01';

NOT 操作符

NOT操作符基本和不等于是类似的,只是NOT操作符是跟在WHERE后面的。

SELECT prod_name, prod_price FROM Products WHERE NOT vend_id = 'DLL01';

上面的语句表示检索所有vend_id列不等于DLL01的行

1.6 通配符

LIKE操作

百分号(%)通配符

%表示任何字符出现任意次数。例如:

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

就表示检索prod_name列以Fish开头的任意字符串的行。

SELECT prod_id, prod_name FROM Products WHERE prod_name LIKE 'F%y';

就表示检索prod_name列以F开头,y结尾的字符串的行。

下划线(_)通配符

下划线的用途和百分号是一样的,唯一的差别是%可以匹配任意多个字符,但是下划线值匹配一个字符,例如:

SELECT prod_id, prod_name FROM Products WHERE prod_name LIKE 'Fish_';

就表示检索prod_name列以Fish开头,后面跟一个任意字符的行。

方括号([])通配符

方括号([])表示用来指定一个字符串,它必须匹配指定位置(通配符的位置)的一个字符。

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

就表示找出J或M开头的cust_contact行。

1.7 拼接字段

字段:基本上与列的意思相同,经常互换使用,不过数据库列一般称为列,而术语字段通常与计算字段一起是使用。

拼接:将值联结到一起(将一个值附加到另一个值)构造单个值。

MySQL数据库拼接字段需要通过Concat方法来进行,例如:

SELECT Concat(vend_name, ' (' , vend_country, ')') FROM Vendors ORDER BY vend_name;

上面的语句,通过Concat方法将1)存储在vend_name列中的名字;2)包含一个空格和一个左圆括号的字符串;3)存储在vend_country列中的国家;4)包含一个右圆括号的字符串拼接在一起。

AS关键字

AS关键字用于给计算字段构建别名,例如上面一条检索语句检索出来的列的名字是Concat(vend_name, ' (' , vend_country, ')'),非常不便于阅读,但是我们可以通过AS关键字来简化这个名字。AS的用法跟在字段的后面,AS后面跟的就是重新赋予的别名,例如:

SELECT Concat(vend_name, ' (' , vend_country, ')') AS vend_title FROM Vendors ORDER BY vend_name;

这样就可以将检索出来的列的名字修改为vend_title了,而不是原来的Concat(vend_name, ' (' , vend_country, ')')

计算字段

有时候我们在一个表里面存储了一张订单的货物数量和货物单价,但是没有存储该订单的总价,这时候我们可以通过计算字段,让数据库帮我们计算总价并返回给我们。

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

上面的语句中quantity*item_price AS total_price表明,计算OrderItems表中20008号订单的总价,将计算后的总价显示为total_price列中。

1.8 常用函数

常用文本处理函数

函数 说明
LEFT()(或使用子字符串函数) 返回字符串左边的字符
LENGTH() (也使用DATALENGTH()或LEN() 返回字符串的长度
LOWER() 将字符串转换为小写
LTRIM() 去掉字符串左边的空格
RIGHT() 返回字符串右边的字符
RTRIM() 去掉字符串右边的空格
SOUNDEX() 返回字符串的SOUNEX值
UPPER() 将字符串转换成大写

数值处理函数

函数 说明
ABS() 返回一个数的绝对值
COS() 返回一个角度的余弦
EXP() 返回一个数的指数值
PI() 返回圆周率
SIN() 返回一个角度的正弦
SQRT() 返回一个数的平方根
TAN() 返回一个角度的正切

1.9 汇总数据

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

COUNT()函数的两种使用方式:

  • 使用COUNT(*)对表中行的数目进行计算,不管表列中包含的是空值(NULL)还是非空值
  • 使用COUNT(column)对特定列中具有值的行进行计算,忽略NULL值

1.10 分组数据

通过COUNT(*)可以检索出某一列包含的数量,但是却不能将相同的行合并在一起。所以,COUNT(*)统计出来的行数就包括了相同的行数了。大家是否觉得可以通过DISTINCT来合并相同的数据行,但是很可惜啊,DISTINCT是不能用于COUNT(*),但是我们可以通过GROUP BY进行合并

SELECT vend_id, COUNT(*) AS num_prods FROM Products GROUP BY vend_id;

这样就能通过GROUP BY来将vend_id列的数据进行合并了。当然我们也可以根据条件对分组进行过滤,不过这里的过滤用的关键字就不再是WHERE了,而是HAVING,例如:

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

那么上面的语句会在前一条语句检索结果的基础上再次进行过滤,这里只会显示数量大于2的分组。

1.11 子查询

一些数据经常是分开存放在不同的表的。例如:

  • 订单的编号、客户的ID和订单日期在表Orders中存储为一行
  • 各个订单包含的物品存储在OrderItems表中
  • 而顾客的详细信息存储在Customers表中

那么我们想知道有哪些顾客订购了'RGAN01'这个上商品,按照上面的表结构呢,我们

  • 首先需要查看表OrderItems中,包含这个物品的订单编号是多少;
  • 然后根据这个订单编号查询Orders表,获得对应客户的ID;
  • 最后再根据这个客户ID来查询Customers表,来获取具体的客户信息

根据上面的过程,第一步使用如下查询语句:

SELECT order_num FROM OrderItems WHERE prod_id = 'RGAN01';

这样就能得到订单编号的ID了,之后我们将订单编号ID(例如是,20007和20008)记录下来,执行下面的查询语句:

SELECT cust_id FROM Orders WHERE order_num IN (20007, 20008);

上面一步,我们已经通过IN关键字来简化了操作的,IN关键字的作用忘记了可以往上看。这一步就能得到cust_id(例如是,100000007和100000008)了,最后还得通过cust_id来获取用户的详细信息啊:

SELECT cust_name, cust_contact FROM Customers WHERE cust_id IN ('1000000007', '1000000008');

经过了上面三步,终于获取到了用户名字cust_name和联系方式了cust_contact,真是艰难。首先要经过三个步骤不说,还要记录中间产生的记录,真是不方便。

鉴于上面的过程,所以我们需要使用子查询来简化上面的操作,其实子查询也没有什么特别的地方,查询的需求和上面的类似,我们直接看查询语句:

SELECT cust_name, cust_contact FROM Customers WHERE cust_id IN 
    ( SELECT cust_id FROM Orders WHERE order_num IN 
        ( SELECT order_num FROM OrderItems WHERE prod_id = 'RGAN01')
    );

因为SQL会将中间的换行和空格去掉,所以上面的语句只是为了好看而做的分行处理。这样一看,其实就是讲三个查询语句组合成一个而已,没什么特别的地方。

用作计算字段的子查询

如果要统计Customers表汇总每个顾客的订单数,需要现获取Customers表中所有的顾客信息,然后再到Orders表中统计订单数。这也是两个查询的过程,而不同的是这里需要统计的是总数。不只是单纯的查询,但是还是可以通过计算字段的子查询来达到。

SELECT cust_name, cust_state,
    ( SELECT COUNT(*) FROM Orders WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;

上面的SELECT COUNT(*) FROM Orders WHERE Orders.cust_id = Customers.cust_id计算字段的子查询作为列存在。

1.12 联结

如果数据存储在多个表中,想要用一条SELECT语句将检索出数据的话,就需要用到联结了。简单说,联结是一种机制,用来在一条SELECT语句中关联表,一次称为联结。

创建联结很简单,指定需要联结的所有表以及关联它们的方式即可。例如:

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

这个SELECT语句和之前所有的语句一样都指定了要检索的列。最大的差别是这些列是来自两个表的,分别是Vendors和Products。它们就是这条SELECT语句联结的两个表的名字。这两个表使用WHERE子句正确的联结,如果没有WHERE子句来进行限定的话,那么得到的行数量解释Vendors表中的行数乘以Products表中的行数。

内联结

上面介绍的联结方式称为等值联结,它是基于两个表之间的相等测试的,这种联结也称为内联结。这种联结还有不同的语法可以使用:

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

上面的语句中,两个表的关系是通过INNER JOIN指定的部分FROM子句,在使用这种语法时,联结条件用特定的ON子句而不是WHERE子句来指出。

另外,联结的表不宜过多,否则数据库的性能会下降的很快。

1.13 组合

组合就是通过UNION来组合多条SQL查询。利用UNION可以给出多条SELECT语句,将他们的结果组合成一个结果集。

大家可以试一试在一个查询里面执行两个SELECT语句的话,最后只会显示最后一条SELECT查询的结果,其他的结果都被最后的查询给覆盖了。所以如果需要通过UNION来组合查询。

用法如下:

SELECT cust_name, cust_contact, cust_email FROM Customers WHERE cust_state IN ('IL', 'IN', 'MI')
UNION
SELECT cust_name, cust_contact, cust_email FROM Customers WHERE cust_name = 'Fun4All';

UNION指示数据库执行这两条SELECT语句,并把输出组合成一个查询结果集。

另外UNION从查询结果集中自动删除重复的行的;换句话说,它的行为和一条SELECT语句中使用多个WHERE子句条件一样。如果想让他返回所有的匹配的行,可以通过UNION ALL关键字:

SELECT cust_name, cust_contact, cust_email FROM Customers WHERE cust_state IN ('IL', 'IN', 'MI')
UNION ALL
SELECT cust_name, cust_contact, cust_email FROM Customers WHERE cust_name = 'Fun4All';

那么符合上面两个SELECT的所有语句都会被列出,包括检索重复的部分。

2、INSERT操作

插入数据由两种方式:

INSERT INTO Customers VALUES (
    '1000000006',
    'Toy Land',
    '123 Any Street',
    'New York',
    'NY',
    '11111',
    'USA',
    NULL,
    NULL);

或者

INSERT INTO Customers (
    cust_id,
    cust_name,
    cust_address,
    cust_city,
    cust_state,
    cust_zip,
    cust_country,
    cust_contact,
    cust_email)
VALUES (
    '1000000006',
    'Toy Land',
    '123 Any Street',
    'New York',
    'NY',
    '11111',
    'USA',
    NULL,
    NULL);

上面两个语句的差别可以看出,第一种方法依赖创建表时SQL语句字段的次序,要是后面有在数据库表中添加字段或者修改字段排序的话,那么第一种INSERT INTO方法就很有可能会报错了,因为和它匹配的数据库表的字段次序已经发生该表了。

而第二种方法在执行INSERT INTO时已经提供了字段名,就算表的结构改变,这条INSERT语句还是可以继续使用的。

其次,使用第二种方法,我们还可以只进行插入部分列的操作。

从另一个表复制到另一个表

在MySQL中通过下面的语句可以进行表的复制操作:

CREATE TABLE CustCopy AS
SELECT * FROM Customers;

3、UPDATE操作

有两种使用UPDATE的方式:

  • 更新表中的特定行
  • 更行表中的所有行

基本的UPDATE语句由三部分组成:

  • 要更新的表
  • 列名和它们的新值
  • 确定要更新的哪些行的过滤条件

例如:

UPDATE Customers
SET cust_contact = 'Sam Roberts',
    cust_email = 'sam@toyland.com'
WHERE cust_id = '1000000006';

4、删除数据

相比之前的UPDATE操作,DELETE就更加简单了:

DELETE FROM Customers
WHERE cust_id = '100000006';

5、 创建表

利用CREATE TABLE创建表,必须给出以下信息:

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   TEXT        NULL
);

6、更新表

通过ALTER TABLE进行表的更新,添加列可以:

ALTER TABLE Vendors ADD vend_phone CHAR(20);

或者,删除列:

ALTER TABLE Vendors DROP COLUMN vend_phone;

7、删除表

DROP TABLE CustCopy;

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d 博主赞过: