SQL基础教程二:基础查询全介绍

这是我参与更文挑战的第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返回所有匹配的行。如何实现返回第一行或一定数量的行呢?

  1. 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分页查询方法及优化的介绍。

  1. SQL Server使用TOPOFFSET

SQL Server普遍使用TOP指定最多返回的行数。以及Access。

-- 只查询前3行的数据
SELECT TOP 3 product_id FROM Product;
复制代码

SQL Server 2012开始,支持ORDER BY ... OFFSET ... FETCH语句,OFFSETFETCH作为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的分页实现由多种方式,不过从性能角度,比较推荐使用OFFSETFETCH子句(语法也简洁)。

同时微软官方文档也是建议使用OFFSETFETCH实现限制返回的行数和作为分页解决方案

关于SQL Server其他的分页方式,比如ROW_NUMBER()NTILE()窗口函数,或借助TOP和TOP子句的实现。

OFFSET等分页的对比和性能比较,可以参考这两篇Sql Server 2012 分页方法分析(offset and fetch)SQL Server2012 T-SQL对分页的增强尝试

  1. Oracle使用行计数器ROWNUM

Oracle通过ROWNUM计算行来实现

SELECT product_id FROM Product
WHERE ROWNUM<=3;
复制代码
  1. 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+NULL10 - NULL1 * NULL4 / NULLNULL / 9NULL / 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=2800NOT 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别名,表示新的拼接字段。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享