这是我参与更文挑战的第6天,活动详情查看: 更文挑战
SELECT语句基础
通过SELECT语句查询并选取出必要数据的过程称为匹配查询或查询(query)。
SELECT查询需要指定选什么和从哪(表)选。
基本的SELECT语句
SELECT <列名>,... FROM <表名>;
复制代码
SELECT语句包含了SELECT和FROM两个子句(clause
)。
子句是SQL语句的组成要素,一个子句通常由一个关键字加上所提供的数据组成,比如SELECT或FROM等开头的短语。
查询结果中列的顺序和SELECT子句中的顺序相同
SELECT子句中列举了希望从表中查询出的列的名称,
FROM子句则指定了选取出数据的表的名称。
SELECT子句可以是列名、常数和计算式。
SELECT product_id, product_name, purchase_price
FROM Product;
复制代码
查询表所有列
星号(*)通配符可以表示所有的列
比如查询所有列:
SELECT * FROM Product;
-- 等同于
SELECT product_id, product_name, product_type, sale_price,
purchase_price, regist_date
FROM Product;
复制代码
使用星号时,列的显示顺序通常是按照
CREATE TABLE
中表定义的列排序。
通常不要使用*通配符,检索不需要的列会减低查询的性能。
SQL中可以在任意单词或子句位置,添加换行符。
为列设定别名
使用AS关键字可以为列设定别名
SELECT product_id AS id, product_name AS 商品名称, purchase_price AS 进货单价
FROM Product;
-- 别名 也可以用双引号(")括起来。不能使用单引号
SELECT product_id AS "id", product_name AS "商品名称", purchase_price AS 进货单价
FROM Product;
-- 别名也可以直接书写,不需要指定AS
SELECT product_id id, product_name 商品名称, purchase_price 进货单价 FROM Product;
复制代码
结果:
id | 商品名称 | 进货单价
------+----------+----------
0001 | T恤 | 500
0002 | 打孔器 | 320
0003 | 运动T恤 | 2800
0005 | 高压锅 | 5000
0006 | 叉子 |
0007 | 擦菜板 | 790
0008 | 圆珠笔 |
0004 | 菜刀 | 8000
复制代码
常数的查询
SELECT子句中还可以写常数。查询的所有行中都会显示常数。
如下,第一列’商品’是字符串常数,第2列38是数字常数,第3列’2009-02-24’是日期常数。
SELECT '商品' AS string, 38 AS number, '2009-02-24' AS date,
product_id, product_name
FROM Product;
复制代码
结果为:
string | number | date | product_id | product_name
--------+--------+------------+------------+--------------
商品 | 38 | 2009-02-24 | 0001 | T恤
商品 | 38 | 2009-02-24 | 0002 | 打孔器
商品 | 38 | 2009-02-24 | 0003 | 运动T恤
商品 | 38 | 2009-02-24 | 0005 | 高压锅
商品 | 38 | 2009-02-24 | 0006 | 叉子
商品 | 38 | 2009-02-24 | 0007 | 擦菜板
商品 | 38 | 2009-02-24 | 0008 | 圆珠笔
商品 | 38 | 2009-02-24 | 0004 | 菜刀
(8 行记录)
复制代码
查询结果中去除重复项
SELECT
子句中使用DISTINCT
可以去除重复项。
去除product_type列中重复的数据:
SELECT DISTINCT product_type
FROM Product;
复制代码
结果为:
product_type
--------------
衣服
办公用品
厨房用具
复制代码
DISTINCT后面有多列时,会将多个列的数据作为组合,去除重复项。
SELECT DISTINCT product_type, regist_date
FROM Product;
复制代码
结果为:
product_type | regist_date
--------------+-------------
衣服 |
厨房用具 | 2008-04-28
衣服 | 2009-09-20
办公用品 | 2009-11-11
厨房用具 | 2009-01-15
办公用品 | 2009-09-11
厨房用具 | 2009-09-20
复制代码
DISTINCT关键字只能用在第一个列名之前。
使用WHERE语句选择记录(过滤数据)
SELECT语句通过WHERE子句来指定查询数据的条件。
SELECT <列名>,...
FROM <表名>
WHERE <条件表达式>; -- 表示查询条件的表达式
复制代码
查询product_type为”衣服”的记录:
SELECT product_name,product_type
FROM Product
WHERE product_type='衣服';
复制代码
结果为:
product_name | product_type
--------------+--------------
T恤 | 衣服
运动T恤 | 衣服
复制代码
查询过程是先通过WHERE子句查询出符合指定条件的记录,然后再选取出SELECT语句指定的列。
WHERE子句必须紧跟在FROM子句之后。
WHERE的作用是实现过滤数据。
过滤可能分为SQL过滤和应用过滤(即返回过多的数据,在应用层对其多虑)。
在客户端过滤数据极其不推荐,因为数据库可以更快速有效地对数据进行过滤,尤其是优化后,而应用层处理数据库的工作不仅影响应用的性能,也限制了应用的可伸缩性;并且数据库服务器会通过网络发送多余的数据,浪费网络带宽。
注释
注释是SQL语句中用来标识说明或者注意事项的部分。
注释的方式有两种:
- 单行注释:写在
--
之后。只能一行 - 多行注释:写在
/*
和*/
之间,可以跨多行
限制结果
默认SELECT返回所有匹配的行。如何实现返回第一行或一定数量的行呢?
- PostgreSQL、MySQL、MariaDB或SQLite使用
LIMIT n1 OFFSET n2
这几种数据库使用LIMIT子句,限制返回的行数
-- 只返回不超过3行的数据
SELECT product_id
FROM Product
LIMIT 3;
复制代码
加上OFFSET
指定开始查询的行数,可实现查询前3行之后的3行数据。
SELECT product_id
FROM Product
LIMIT 3 OFFSET 3;
复制代码
这就是最常见的”分页”功能。
限制返回的行数,通常要结合Order By排序子句使用。因为如果不指定排序的话,返回结果就可能没固定的顺序,指定行数量也就没多大意义。
LIMIT
子句放在WHERE
/ORDER BY
之后
第0行:第一个检索的是第0行,如,
LIMIT 1 OFFSET 1
查询的是第二行。将OFFSET
看做”偏移”更稳妥,即查询偏移0行之后的n行,查询偏移1行之后的n行,易于理解,且更语义化。
MySQL/MariaDB和SQLite的
LIMIT
简洁语法:
LIMIT n1 OFFSET n2
简写为:LIMIT n2,n1
LIMIT 4 OFFSET 3
可以写成LIMIT 3,4
关于使用OFFSET偏移量的性能问题,一般偏移量越大,查询时间越慢。
关于MySQL的分页优化和速度问题,可以查看下这篇MySQL分页查询方法及优化的介绍。
SQL Server
使用TOP
或OFFSET
SQL Server
普遍使用TOP
指定最多返回的行数。以及Access。
-- 只查询前3行的数据
SELECT TOP 3 product_id FROM Product;
复制代码
SQL Server 2012
开始,支持ORDER BY ... OFFSET ... FETCH
语句,OFFSET
和FETCH
作为ORDER BY
可选子句使用,不能单独使用。
语法如下,其中FIRST和NEXT是同义词,没有区别,可互换使用。ROW和ROWS两者没有区别,两者也可互换:
ORDER BY column_list [ASC |DESC]
OFFSET offset_row_count {ROW | ROWS}
FETCH {FIRST | NEXT} fetch_row_count {ROW | ROWS} ONLY
复制代码
fetch_row_count可以是integer或结果为integer的表达式
选择前3行之后的数据(跳过前3行)
SELECT product_id
FROM Product
ORDER BY product_id
OFFSET 3 ROWS; -- 写成 OFFSET 3 ROW 是等效的
复制代码
选择前3行的数据
SELECT product_id
FROM Product
ORDER BY product_id
OFFSET 0 ROWS
FETCH NEXT 3 ROWS ONLY; -- 等同于 FETCH FIRST 3 ROWS ONLY;
-- 等同 TOP 3
复制代码
选择前3行之后的4行数据
SELECT product_id
FROM Product
ORDER BY product_id
OFFSET 3 ROWS
FETCH NEXT 4 ROWS ONLY;
复制代码
关于SQL Server的分页实现由多种方式,不过从性能角度,比较推荐使用
OFFSET
和FETCH
子句(语法也简洁)。同时微软官方文档也是建议使用
OFFSET
和FETCH
实现限制返回的行数和作为分页解决方案关于SQL Server其他的分页方式,比如
ROW_NUMBER()
、NTILE()
窗口函数,或借助TOP和TOP子句的实现。
OFFSET
等分页的对比和性能比较,可以参考这两篇Sql Server 2012 分页方法分析(offset and fetch)、SQL Server2012 T-SQL对分页的增强尝试
- Oracle使用行计数器
ROWNUM
Oracle通过ROWNUM
计算行来实现
SELECT product_id FROM Product
WHERE ROWNUM<=3;
复制代码
- DB2使用FETCH语句
SELECT product_id FROM Product
FETCH FIRST 5 ROWS ONLY;
复制代码
算术运算符和比较运算符
算术运算符
SQL语句中可以使用计算表达式(运算表达式)。运算是以行为单位执行的。
如下,把商品单价的2倍查询出来
SELECT product_name, sale_price, sale_price*2 AS sale_price_x2
FROM Product;
复制代码
结果为:
product_name | sale_price | sale_price_x2
--------------+------------+---------------
T恤 | 1000 | 2000
打孔器 | 500 | 1000
运动T恤 | 4000 | 8000
高压锅 | 6800 | 13600
叉子 | 500 | 1000
擦菜板 | 880 | 1760
圆珠笔 | 100 | 200
菜刀 | 3000 | 6000
复制代码
四则运算所使用的运算符(+、-、*、/
)称为算术运算符。
运算符就是使用其两边的值进行四则运算或者字符串拼接、数值大小比较等运算,并返回结果的符号。
运算表达式中可以使用括号
()
。SQL的任何表达式中都可以使用括号。
含NULL的运算表达式
所有包含NULL的计算,结果都是NULL
如下,运算结果都是NULL
5+NULL
、10 - NULL
、1 * NULL
、 4 / NULL
、NULL / 9
、 NULL / 0
用NULL除以0时这一原则也适用,即不会报错,结果为NULL。
关于SELECT中的FROM子句:
通常SELECT语句中会包含FROM子句。但是有时也可以只使用SELECT子句进行计算。通常用来得到一行临时数据。
-- SQL Server | PostgreSQL | MySQL SELECT (100 + 200)*3 AS calculation; 复制代码
但是OracIe中不允许省略SELECT语句中的FROM子句,可以使用DUAL临时表。
比较运算符
选取出sale_price列为500的记录:
SELECT product_name,product_type
FROM Product
WHERE sale_price=500;
复制代码
用来比较其两边的列或者值的符号称为比较运算符。
符号=
就是比较运算符,<>
表示”不等于”,>=
大于等于,>
大于,<=
小于等于,<
小于。
有的DBMS也支持
!=
(不等于)、!<
不小于、!>
不大于等操作符
还可以使用比较运算符对计算结果进行比较,
如下,在WHERE的条件表达式中,指定销售单间(sale_price)比进货单价(purchase_price)高出500的条件
SELECT product_name,sale_price,purchase_price
FROM Product
WHERE sale_price - purchase_price >= 500;
复制代码
结果如下:
product_name | sale_price | purchase_price
--------------+------------+----------------
T恤 | 1000 | 500
运动T恤 | 4000 | 2800
高压锅 | 6800 | 5000
复制代码
对字符串使用不等号
字符串使用大于等于
或者小于等于
不等号时,需要注意字符串的比较并不和数字一样,是通过字符次序依次按照字典顺序比较,与数字顺序不一样。
比较时,按照条目在字典中出现的顺序来进行排序。
如下,创建表Chars并插入数据
-- DDL:创建表
CREATE TABLE Chars
(chr CHAR(3) NOT NULL,
PRIMARY KEY (chr));
--SQL Server PostgreSQL
-- DML:插入数据
BEGIN TRANSACTION;
INSERT INTO Chars VALUES ('1'),
('2'),
('3'),
('10'),
('11'),
('222');
COMMIT;
复制代码
查询大于’2’的数据
SELECT chr
FROM Chars
WHERE chr>'2';
复制代码
查询的结果只有’3’和’222’两条记录
chr
-----
3
222
复制代码
不能对NULL使用比较运算符(NULL值的过滤)
使用比较运算符时,如果查询条件的列中包含NULL时,需要注意,其比较结果永远为false。比较运算符,无法选出列为NULL的记录。
比如Product表的purchase_price
列,有两个值为NULL的记录,如果查询进货单价不是2800的记录,结果如下。
SELECT product_name,purchase_price
FROM Product
WHERE purchase_price <> 2800;
复制代码
结果中并不会出现不是2800的NULL的记录
product_name | purchase_price
--------------+----------------
T恤 | 500
打孔器 | 320
高压锅 | 5000
擦菜板 | 790
复制代码
即使使用purchase_price=NULL
也不会选出为NULL的记录
SQL不识别
=NULL
、<>NULL
要想判断是否为NULL,需要使用IS NULL
运算符;判断不为NULL,使用IS NOT NULL
SELECT product_name,purchase_price
FROM Product
WHERE purchase_price IS NULL;
复制代码
结果为:
product_name | purchase_price
--------------+----------------
叉子 |
圆珠笔 |
复制代码
通过过滤,选择不包含指定值的所有行时,如果也希望返回含NULL值的行。则一定要注意。单独加上对NULL的过滤。
逻辑运算符
NOT运算符
NOT运算符表示否定,比<>
使用更广泛。但是不能单独使用,要与其他条件组合起来。
大多数DBMS允许使用NOT否定任何条件。
比如,选取sale_price大于等于1000的记录
SELECT product_name, product_type, sale_price
FROM Product
WHERE sale_price >= 1000;
复制代码
结果为:
product_name | product_type | sale_price
--------------+--------------+------------
T恤 | 衣服 | 1000
运动T恤 | 衣服 | 4000
高压锅 | 厨房用具 | 6800
菜刀 | 厨房用具 | 3000
复制代码
查询条件中添加NOT,将显示所有小于1000的记录:
SELECT product_name, product_type, sale_price
FROM Product
WHERE NOT sale_price >= 1000;
复制代码
AND和OR运算符
组合多个查询条件。
-
AND运算符在其两侧的查询条件都成立时整个查询条件才成立,相当于“并且”。
-
OR运算符在其两侧的查询条件有一个成立时整个查询条件都成立,相当于“或者”
如下,AND查询。
SELECT product_name,purchase_price
FROM Product
WHERE product_type='厨房用具'
AND sale_price>=3000;
复制代码
该查询的文氏图如下所示,两个圆重合的部分就是AND运算符选取到的记录。
文氏图(英语:Venn diagram),或译Venn图、温氏图、维恩图、范氏图,是在所谓的集合论(或者类的理论)数学分支中,在不太严格的意义下用以表示集合(或类)的一种草图。
OR查询:
SELECT product_name,purchase_price
FROM Product
WHERE product_type='厨房用具'
OR sale_price>=3000;
复制代码
OR查询的文氏图如下,两个查询条件中满足任何一个的商品就是OR运算符能选取到的记录。
括号的使用(求值顺序)
如果查询“商品种类为办公用品”并且“登记日期是2009年9月11日或者2009年9月20日”的记录,该怎么写查询条件呢?
SELECT product_name,product_type,regist_date
FROM Product
WHERE product_type='办公用品'
AND regist_date='2009-09-11'
OR regist_date='2009-09-20';
复制代码
上面的SQL语句因为AND运算符先执行,最终的查询条件会变为:
product_type='办公用品' AND regist_date='2009-09-11'
OR
regist_date='2009-09-20'
复制代码
即“商品种类为办公用品,并且登记日期是2009年9月11日”或者“登记日期是2009年9月20日”的记录。
正确方法是使用()
,让OR运算作为整体执行
SELECT product_name,product_type,regist_date
FROM Product
WHERE product_type='办公用品'
AND (regist_date='2009-09-11'
OR regist_date='2009-09-20');
复制代码
结果如下:
product_name | product_type | regist_date
--------------+--------------+-------------
打孔器 | 办公用品 | 2009-09-11
复制代码
圆括号具有比AND或OR操作符更高的求值顺序。这样,SQL语句执行时,会先过滤圆括号内的条件
AND运算符的优先级高于OR运算符。大多数语言一样也都一样。也就是,在处理OR操作符前,会优先处理AND操作符。
所以查询无法通过简单的调整AND和OR的顺序,修改查询。
逻辑运算符和真值
NOT、AND和OR称为逻辑运算符,此处的逻辑就是操作真值,逻辑运算符对比较运算符等返回的真值进行操作。
真值是值为TRUE
(真)或FALSE
(假)其中之一的值。
真值表(truth table
,或logic table
)是对真值逻辑操作及其结果的表。
使用AND运算符进行的逻辑运算称为逻辑积。
使用OR运算符进行的逻辑运算称为逻辑和。
含有NULL时的真值
逻辑运算要注意NULL值。
Product表中,0006号叉子和0008号圆珠笔的进货单价purchase_price
为NULL,但是purchase_price=2800
和NOT purchase_price=2800
都不能选取为NULL的记录。原因是NULL表示的是不确定(UNKNOWN
)。真假之外的第三种值。
通常的逻辑运算都是二值逻辑。但只有SQL中的逻辑运算称为三值逻辑。
包含”不确定”的完整真值表如下:
原本的真值表只有4行,加入NULL后就变为3*3=9行。
在数据库中”尽量不使用NULL”是一种共识。
复杂的过滤(条件)
BETWEEN操作符(范围值检查)
BETWEEN操作符用于检查某个范围的值。BETWEEN关键字后面跟随AND连接的两个值,即开始值和结束值。
==BETWEEN匹配范围中所有的值,包括指定的开始值和结束值。==
查询售价500~1000之间的产品。
select * from product where sale_price between 500 and 1000;
-- 等同于:select * from product where sale_price >= 500 and sale_price<=1000;
复制代码
结果:
product_id | product_name | product_type | sale_price | purchase_price | regist_date
------------+--------------+--------------+------------+----------------+-------------
0001 | T恤 | 衣服 | 1000 | 500 | 2009-09-20
0002 | 打孔器 | 办公用品 | 500 | 320 | 2009-09-11
0006 | 叉子 | 厨房用具 | 500 | | 2009-09-20
0007 | 擦菜板 | 厨房用具 | 880 | 790 | 2008-04-28
0009 | T恤 | 衣服 | 1000 | 500 | 2009-09-20
(5 行记录)
复制代码
IN操作符(条件范围)
IN指定某个条件的范围,在这个范围内进行匹配。IN范围由一组逗号分隔、括在圆括号中的合法值组成。
如下,取销售值在(500,1000,2000,3000)
范围内的产品:
select * from product where sale_price in (500,1000,2000,3000);
复制代码
结果:
product_id | product_name | product_type | sale_price | purchase_price | regist_date
------------+--------------+--------------+------------+----------------+-------------
0001 | T恤 | 衣服 | 1000 | 500 | 2009-09-20
0002 | 打孔器 | 办公用品 | 500 | 320 | 2009-09-11
0006 | 叉子 | 厨房用具 | 500 | | 2009-09-20
0004 | 菜刀 | 厨房用具 | 3000 | 2800 | 2009-09-20
0009 | T恤 | 衣服 | 1000 | 500 | 2009-09-20
(5 行记录)
复制代码
IN操作符完成的是与OR相同的功能。上面IN查询等同于:
select * from product where sale_price=500 OR sale_price=1000 OR sale_price=2000 OR sale_price=3000;
复制代码
用IN操作符优点:
- 在匹配多个合法值时,IN的语法更清楚直观。
- 与AND和OR操作符组合使用IN时,求值顺序更容易管理。
- IN操作符一般比一组OR操作符执行得更快。
- IN的最大优点是可以包含其他SELECT语句,能够更动态地建立WHERE子句。
通配符过滤
LIKE操作符
之前WHERE过滤的操作符都是对已知值进行匹配。比如一个值或多个值,大于或小于某个值,某些范围的值等,都是对已知值进行过滤。
但是,有时需要搜索包含某些文本,但是不知道具体值的数据。即过滤的值并不确定,简单的比较操作符则不行,需要使用通配符。
通配符是SQL的WHERE子句中有特殊含义的字符。
使用通配符,可以创建匹配特定数据的搜索模式。
- 通配符(
wildcard
):用来匹配值的一部分的特殊字符。搜索模式(
search pattern
):由字面值、通配符或两者组合构成的搜索条件。
通配符要在LIKE操作符中使用。LIKE标识,后面的搜索模式利用通配符匹配而不是简单的相等匹配进行比较。
技术上,LIKE是谓词(
predicate
)而不是操作符。但其和操作符结果相同。
通配符搜索只能用于文本字段(字符串),非文本数据类型的字段不能使用通配符搜索。
百分号(%)通配符
百分号%表示任何字符出现任意次数。
比如,查找以T
开头的产品:
select * from product where product_name like 'T%';
复制代码
product_id | product_name | product_type | sale_price | purchase_price | regist_date
------------+--------------+--------------+------------+----------------+-------------
0001 | T恤 | 衣服 | 1000 | 500 | 2009-09-20
0009 | T恤 | 衣服 | 1000 | 500 | 2009-09-20
(2 行记录)
复制代码
搜索模式’T%’检索任意以T开头的内容。
Microsoft Access任意字符串通配符使用*而不是%。
通配符可在搜索模式中的任意位置使用,并且可以使用多个通配符。
如下使用两个通配符,位于搜索模式的两端。
select * from product where product_type like '%用%';
复制代码
product_id | product_name | product_type | sale_price | purchase_price | regist_date
------------+--------------+--------------+------------+----------------+-------------
0002 | 打孔器 | 办公用品 | 500 | 320 | 2009-09-11
0005 | 高压锅 | 厨房用具 | 6800 | 5000 | 2009-01-15
0006 | 叉子 | 厨房用具 | 500 | | 2009-09-20
0007 | 擦菜板 | 厨房用具 | 880 | 790 | 2008-04-28
0008 | 圆珠笔 | 办公用品 | 100 | | 2009-11-11
0004 | 菜刀 | 厨房用具 | 3000 | 2800 | 2009-09-20
(6 行记录)
复制代码
%也可以匹配 0个字符。
通配符%无法匹配NULL。
where product_name like '%'
不会匹配产品名称为NULL的行。
下划线(_)通配符
下划线(_)只匹配单个字符。
DB2不支持通配符_;Microsoft Access中需要使用?而不是_。
select * from product where product_name like 'T_';
复制代码
方括号([])通配符。用来指定一个字符集,它匹配指定位置(通配符的位置)的一个字符(集合中的一个字符)。
只有Access和 SQL Server支持方括号集合
通配符使用建议
SQL的通配符在模糊匹配中很有用。但是通配符搜索一般比其他搜索要耗费更长的处理时间。
- 不要过度使用通配符。
- 通配符尽量不要用在搜索模式的开始处。把通配符置于开始处,搜索起来最慢。
- 注意通配符的位置。否则容易搜索出错。
计算字段
计算字段不是数据库表中实际的字段值,它是在执行SELECT语句时进行计算操作创建的。
拼接字段
拼接(concatenate
)就是将多个值联接到一起构成单个值(将一个值附加到另一个值)。
拼接可以是多个列或字符串。
如下,拼接产品类型和产品名为”类型-名称”。
-- DB2、Oracle、PostgreSQL、SQLite 使用||拼接字符串
select product_type||'-'||product_name from product;
-- Access和 SQL Server使用+拼接
select product_type+'-'+product_name from product;
-- MySQL、MariaDB只能使用concat函数
select concat(product_type,'-',product_name) from product;
复制代码
结果:
?column?
-----------------
衣服-T恤
办公用品-打孔器
衣服-运动T恤
厨房用具-高压锅
厨房用具-叉子
厨房用具-擦菜板
办公用品-圆珠笔
厨房用具-菜刀
衣服-T恤
(9 行记录)
复制代码
concat字符串拼接函数,SQL Server、PostgreSQL、MySQL、MariaDB、Oracle等大多数DBMS都支持。
算术计算
使用算术运算符(+、-、*、/
)可以对字段进行算术计算。
拼接字段或对字段进行算术计算时,通常使用AS别名,表示新的拼接字段。