[数据库] SQL 语法之进阶篇
一、创建计算字段
下面介绍什么是计算字段,如何创建计算字段,以及如何从应用程序中使用别名引用它们。
1.1 计算字段
存储在数据库表中的数据一般不是应用程序所需要的格式,下面举几个例子。
需要显示公司名,同时还需要显示公司的地址,但这两个信息存储在不同的表列中。
物品订单表存储物品的价格和数量,不存储每个物品的总价格(用价格乘以数量即可)。但为打印发票,需要物品的总价格。
需要根据表数据进行诸如总数、平均数的计算。
存储在表中的数据可能不是应用程序所需要的,我们需要直接从数据库中检索出转换、计算或格式化过的数据,而不是检索出数据,然后再在客户端应用程序中重新格式化。这就是计算字段可以派上用场的地方了。与前面介绍的列不同,计算字段并不实际存在于数据库表中。计算字段是运行时在 SELECT 语句内创建的。
1.2 拼接字段
为了说明如何使用计算字段,我们来举一个简单例子,创建由两列组成的标题。在 SQL 中的 SELECT 语句中,可使用一个特殊的操作符来拼接两个列。根据你所使用的 DBMS,此操作符可用加号(+)或两个竖杠(II)表示。在 MySQL 和 MariaDB 中,必须使用特殊的函数。
下面是 MySQL 和 MariaDB 中使用的语句:
SELECT Concat(vend_name, ' ', vend_country) FROM Vendors;
使用 Concat 函数拼接 vend_name,一个空格 ' ' 和 vend_country。
使用别名
SELECT 语句可以很好地拼接地址字段,但这个新计算列的名字是什么呢?实际上它没有名字,它只是一个值。如 果仅在 SQL 查询工具中查看一下结果,这样没有什么不好。但是,一个未命名的列不能用于客户端应用中,因为客户端没有办法引用它。
为了解决这个问题,SQL 支持列别名。别名(alias)是一个字段或值的替换名。别名用 AS 关键字赋予。请看下面的 SELECT 语句:
SELECT Concat(vend_name, ' ', vend_country) AS vend_title FROM Vendors;
这里的计算字段之后跟了文本 AS vend_title,它指示 SQL 创建一个包含指定计算结果的名为 vend_title 的计算字段。现在列名为 vend_title,任何客户端应用都可以按名称引用这个列,就像它是一个实际的表列一样。
1.3 执行算数计算
计算字段的另一常见用途是对检索出的数据进行算术计算。例如,可以汇总 2008 年这一年订单物品的价格(单价乘以订购数量):
SELECT prod_id, quantity, item_price,quantity*item_price AS expanded_price FROM OrderItems WHERE order_num = 20008;
这里的计算字段之后跟了文本 AS vend_title,它指示 SQL 创建一个包含指定计算结果的名为 vend_title 的计算字段。现在列名为 vend_title,任何客户端应用都可以按名称引用这个列,就像它是一个实际的表列一样。
SQL 支持基本的加减乘除这4种算术操作符。
二、使用数据处理函数
下面介绍什么是函数,DBMS 支持何种函数,以及如何使用这些函数;还将讲解为什么 SQL 函数的使用可能会带来问题。
2.1 函数
与大多数其他计算机语言一样,SQL 也可以用函数来处理数据。函数一般是在数据上执行的,为数据的转换和处理提供了方便。
函数带来的问题
与几乎所有 DBMS 都等同地支持 SQL 语句(如 SELECT)不同,每一个 DBMS 都有特定的函数。事实上,只有少数几个函数被所有主要的 DBMS 等同地支持。虽然所有类型的函数一般都可以在每个 DBMS 中使用,但各个函数的名称和语法可能极其不同。这就表示为特定 SQL 实现编写的代码在其他实现中可能不正常。
8.2 使用函数
大多数 SQL 实现支持以下类型的函数。
- 用于处理文本字符串(如删除或填充值,转换值为大写或小写)的文本函数。
- 用于在数值数据上进行算术操作(如返回绝对值,进行代数运算)的数值函数。
- 用于处理日期和时间值并从这些值中提取特定成分(如返回两个日期之差,检查日期有效性)的日期和时间函数。
- 返回 DBMS 正使用的特殊信息(如返回用户登录信息)的系统函数。
1. 文本处理函数
下面例子使用的是 UPPER() 函数:
SELECT vend_name, UPPER(vend_name) AS vend_name_upcase FROM Vendors;
UPPER() 将文本转换为大写。
下表列出了一些常用的文本处理函数。
函 数 | 说 明 |
---|---|
LEFT()(或使用子字符串函数) | 返回字符串左边的字符 |
LENGTH() (也使用 DATALENGTH() 或 LEN()) | 返回字符串的长度 |
LOWER() (Access 使用 LCASE()) | 将字符串转换为小写 |
LTRIM() | 去掉字符串左边的空格 |
RIGHT() (或使用子字符串函数) | 返回字符串右边的字符 |
RTRIM() | 去掉字符串右边的空格 |
SOUNDEX() | 返回字符串的SOUNDEX值 |
UPPER() (Access 使用 UCASE()) | 将字符串转换为大写 |
2. 日期和时间处理函数
MySQL 和 MariaDB 用户可使用名为 YEAR() 的函数从日期中提取年份:
SELECT order_num FROM Orders WHERE YEAR(order_date) = 2012;
DBMS 提供的功能远不止简单的日期成分提取。大多数 DBMS 具有比较日期、执行基于日期的运算、选择日期格式等的函数。但是,可以看到,不同 DBMS 的日期-时间处理函数可能不同。关于具体 DBMS 支持的日期-时间处理函数,请参阅相应的文档。
3. 数值处理函数
数值处理函数仅处理数值数据。这些函数一般主要用于代数、三角或几何运算,因此不像字符串或日期-时间处理函数使用那么频繁。具有讽刺意味的是,在主要 DBMS 的函数中,数值函数是最一致、最统一的函数。下表列出了一些常用的数值处理函数。
函 数 | 说 明 |
---|---|
ABS() | 返回一个数的绝对值 |
COS() | 返回一个角度的余弦 |
EXP() | 返回一个数的指数值 |
PI() | 返回圆周率 |
SIN() | 返回一个角度的正弦 |
SQRT() | 返回一个数的平方根 |
TAN() | 返回一个角度的正切 |
三、汇总数据
下面介绍什么是 SQL 的聚集函数,如何利用它们汇总表的数据。
3.1 聚集函数
我们经常需要汇总数据而不用把它们实际检索出来,为此 SQL 提供了专门的函数。使用这些函数,SQL 查询可用于检索数据,以便分析和报表生成。这种类型的检索例子有:
- 确定表中行数(或者满足某个条件或包含某个特定值的行数);
- 获得表中某些行的和;
- 找出表列(或所有行或某些特定的行)的最大值、最小值、平均值。
为方便这种类型的检索,SQL 给出了 5 个聚集函数,见下表。这些函数能进行上述检索。与前一章介绍的数据处理函数不同,SQL 的聚集函数在各种主要 SQL 实现中得到了相当一致的支持。
函 数 | 说 明 |
---|---|
AVG() | 返回某列的平均值 |
COUNT() | 返回某列的行数 |
MAX() | 返回某列的最大值 |
MIN() | 返回某列的最小值 |
SUM() | 返回某列值之和 |
1. AVG() 函数
AVG() 通过对表中行数计数并计算其列值之和,求得该列的平均值。下面的例子使用 AVG() 返回 Products 表中所有产品的平均价格:
SELECT AVG(prod_price) AS avg_price FROM Products WHERE vend_id = 'DLL01';
此 SELECT 语句返回值 avg_price,它包含 Products 表中所有产品的平均价格。
2. COUNT() 函数
C0UNT() 函数进行计数。可利用 C0UNT() 确定表中行的数目或符合特定条件的行的数目。C0UNT() 函数有两种使用方式:
- 使用 C0UNT() 对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值。
- 使用 COUNT(column) 对特定列中具有值的行进行计数,忽略 NULL 值。
SELECT COUNT(*) AS num_cust FROM Customers;
在此例子中,利用 C0UNT(*) 对所有行计数,不管行中各列有什么值。计数值在 num_cust 中返回。
3.2 聚集不同值
以上 5 个聚集函数都可以如下使用:
- 对所有行执行计算,指定 ALL 参数或不指定参数(因为 ALL 是默认行为)。
- 只包含不同的值,指定 DISTINCT 参数。
下面的例子使用 AVG() 函数返回特定供应商提供的产品的平均价格。它与上面的 SELECT 语句相同,但使用了 DISTINCT 参数,因此平均值只考虑各个不同的价格:
SELECT AVG(DISTINCT prod_price) AS avg_price FROM Products WHERE vend_id = 'DLL01';
使用了 DISTINCT 后,会排除相同的价格。
3.3 组合聚集函数
目前为止的所有聚集函数例子都只涉及单个函数。但实际上,SELECT 语句可根据需要包含多个聚集函数。请看下面的例子:
SELECT COUNT(*) AS num_items,MIN(prod_price) AS price_min, MAX(prod_price) AS price_max, AVG(prod_price) AS price_avg FROM Products;
这里用单条 SELECT 语句执行了 4 个聚集计算,返回 4 个值(Products 表中物品的数目,产品价格的最高值、最低值以及平均值)。
四、分组数据
下面介绍如何分组数据,以便汇总表内容的子集。这涉及两个新 SELECT 语句子句:GROUP BY 子句和 HAVING 子句。
4.1 数据分组
如果要返回每个供应商提供的产品数目,该怎么办?或者返回只提供一项产品的供应商的产品,或者返回提供 10 个以上产品的供应商的产品, 怎么办?这就是分组大显身手的时候了。使用分组可以将数据分为多个逻辑组,对每个组进行聚集计算。
4.2 创建分组
分组是使用 SELECT 语句的 GROUP BY 子句建立的。理解分组的最好办法是看一个例子:
SELECT vend_id,COUNT(*) AS num_prods FROM Products GROUP BY vend_id;
输出如下:
+---------+-----------+
| vend_id | num_prods |
+---------+-----------+
| BRS01 | 3 |
| DLL01 | 4 |
| FNG01 | 2 |
+---------+-----------+
上面的 SELECT 语句指定了两个列:vend_id 包含产品供应商的 ID,num_prods 为计算字段,GROUP BY 子句指示 DBMS 按 vend_id 排序并分组数据。这就会对每个 vend_id 而不是整个表计算 num_prods —次。
4.3 过滤分组
除了能用 GROUP BY 分组数据外,SQL 还允许过滤分组,规定包括哪些分组,排除哪些分组。例如,你可能想要列出至少有两个订单的所有顾客。
SQL 为此提供了另一个子句,就是 HAVING 子句。HAVING 非常类似于 WHERE。事实上,目前为止所学过的所有类型的 WHERE 子句都可以用 HAVING 来替代。唯一的差别是,WHERE 过滤行,而 HAVING 过滤分组。
SELECT cust_id,COUNT(*) AS orders FROM Orders GROUP BY cust_id HAVING COUNT(*) >= 2;
这条 SELECT 语句的前三行类似于上面的语句。最后一行增加了 HAVING 子句,它过滤 COUNT(*) >= 2 (两个以上订单)的那些分组。
五、联结表
下面会介绍什么是联结,为什么使用联结,如何编写使用联结的 SELECT 语句。
5.1 联结
SQL 最强大的功能之一就是能在数据查询的执行中联结(join)表。联结是利用 SQL 的 SELECT 能执行的最重要的操作,很好地理解联结及其语法是学习 SQL 的极为重要的部分。
1. 关系表
有一个包含产品目录的数据库表,其中每类物品占一行。对于每一种物品,要存储的信息包括产品描述、价格,以及生产该产品的供应商。
现在有同一供应商生产的多种物品,那么在何处存储供应商名、地址、联系方法等供应商信息呢?将这些数据与产品信息分开存储的理由是:
- 同一供应商生产的每个产品,其供应商信息都是相同的,对每个产品重复此信息既浪费时间又浪费存储空间;
- 如果供应商信息发生变化,例如供应商迁址或电话号码变动,只需修改一次即可;
- 如果有重复数据(即每种产品都存储供应商信息),则很难保证每次输入该数据的方式都相同。不一致的数据在报表中就很难利用。
关系表的设计就是要把信息分解成多个表,一类数据一个表。各表通过某些共同的值互相关联(所以才叫关系数据库)。
在这个例子中可建立两个表:一个存储供应商信息,另一个存储产品信息。Vendors 表包含所有供应商信息,每个供应商占一行,具有唯一的标识。
Products 表只存储产品信息,除了存储供应商 ID (Vendors 表的主键)外,它不存储其他有关供应商的信息。Vendors 表的主键将 Vendors 表与 Products 表关联,利用供应商 ID 能从 Vendors 表中找出相应供应商的详细信息。
2. 为什么使用联结
如前所述,将数据分解为多个表能更有效地存储,更方便地处理,并且可伸缩性更好。但这些好处是有代价的。如果数据存储在多个表中,怎样用一条 SELECT 语句就检索出数据呢?
答案是使用联结。简单说,联结是一种机制,用来在一条 SELECT 语句中关联表,因此称为联结。使用特殊的语法,可以联结多个表返回一组输出,联结在运行时关联表中正确的行。
5.2 创建联结
创建联结非常简单,指定要联结的所有表以及关联它们的方式即可。请看下面的例子:
SELECT vend_name, prod_name, prod_price FROM Vendors, Products WHERE Vendors.vend_id = Products.vend_id;
SELECT 语句与前面所有语句一样指定要检索的列。这里最大的差别是所指定的两列(prod_name 和 prod_price)在一个表中,而第三列(vend_name)在另一个表中。
这条语句的 FROM 子句列出了两个表:Vendors 和 Products。它们就是这条 SELECT 语句联结的两个表的名字。这两个表用 WHERE 子句正确地联结,WHERE 子句指示 DBMS 将 Vendors 表中的 vend_id 与 Products 表中的 vend_id 匹配起来。
可以看到,要匹配的两列指定为 Vendors.vend_id 和 Products.vend_id。这里需要这种完全限定列名,如果只给出 vend_id,DBMS 就不知道指的是哪一个(每个表中有一个)。
5.3 联结多个表
SQL 不限制一条 SELECT 语句中可以联结的表的数目。创建联结的基本规则也相同。首先列出所有表,然后定义表之间的关系。例如:
SELECT prod_name,vend_name, prod_price, quantity FROM OrderItems, Products, Vendors WHERE Products.vend_id = Vendors.vend_id AND OrderItems.prod_id = Products.prod_id AND order_num = 20007;
这个例子显示订单 20007 中的物品。订单物品存储在 OrderItems 表中。每个产品按其产品 ID 存储,它引用 Products 表中的产品。这些产品通过供应商 ID 联结到 Vendors 表中相应的供应商,供应商 ID 存储在每个产品的记录中。这里的 FROM 子句列出三个表,WHERE 子句定义这两个联结条件, 而第三个联结条件用来过滤出订单 20007 中的物品。
六、创建高级联结
本课讲解另外一些联结(包括它们的含义和使用方法),介绍如何使用表别名,如何对被联结的表使用聚集函数。
6.1 使用表别名
请看下面的 SELECT 语句。它与前一课例子中所用的语句基本相同,但改成了使用表别名:
SELECT cust_name, cust_contact FROM Customers AS C, Orders AS O, OrderItems AS OI WHERE C.cust_id = O.cust_id AND OI.order_num = O.order_num AND prod_id = 'RGAN01';
可以看到,FROM 子句中的三个表全都有别名。Customers AS C 使用 C 作为 Customers 的别名,如此等等。这样,就可以使用省略的 C 而不用全名 Customers。在这个例子中,表别名只用于 WHERE 子句。其实它不仅能用于 WHERE 子句,还可以用于 SELECT 的列表、ORDER BY 子句以及其他语句部分。
6.2 使用不同类型的联结
迄今为止,我们使用的只是内联结或等值联结的简单联结。现在来看三种其他联结:自联结(self-join)、自然联结(natura join)和外联结(outer join)。由于篇幅原因,这里不再过多介绍。
七、使用子查询
下面介绍什么是子查询,如何使用它们。
7.1 子查询
SQL 还允许创建子查询(subquery),即嵌套在其他查询中的查询。为什么要这样做呢?理解这个概念的最好方法是考察几个例子。
7.2 利用子查询进行过滤
订单存储在两个表中。每个订单包含订单编号、客户 ID、订单日期,在 Orders 表中存储为一行。各订单的物品存储在相关的 Orderltems 表中。Orders 表不存储顾客信息,只存储顾客 ID。顾客的实际信息存储在 Customers 表中。
现在,假如需要列出订购物品 RGAN01 的所有顾客,应该怎样检索?下面列出具体的步骤。
检索包含物品 RGAN01 的所有订单的编号。
检索具有前一步骤列出的订单编号的所有顾客的 ID。
检索前一步骤返回的所有顾客 ID 的顾客信息。
上述每个步骤都可以单独作为一个查询来执行。可以把一条 SELECT 语句返回的结果用于另一条 SELECT 语句的 WHERE 子句。也可以使用子查询来把 3 个查询组合成一条语句。使用子查询的语句如下所示:
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'));
为了执行上述 SELECT 语句,DBMS 实际上必须执行三条 SELECT 语句。最里边的子查询返回订单号列表,此列表用于其外面的子查询的 WHERE 子句。外面的子查询返回顾客 ID 列表,此顾客 ID 列表用于最外层查询的 WHERE 子句。最外层查询返回所需的数据。
八、组合查询
本课讲述如何利用 UNION 操作符将多条 SELECT 语句组合成一个结果集。
8.1 组合查询
多数 SQL 查询只包含从一个或多个表中返回数据的单条 SELECT 语句。但是,SQL 也允许执行多个查询(多条 SELECT 语句),并将结果作为一个查询结果集返回。这些组合查询通常称为并(union)或复合查询(compound query)。
主要有两种情况需要使用组合查询:
在一个查询中从不同的表返回结构数据;
对一个表执行多个查询,按一个查询返回数据。
8.2 创建组合查询
可用 UNION 操作符来组合数条 SQL 查询。利用 UNION,可给出多条 SELECT 语句,将它们的结果组合成一个结果集。如下所示:
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';
这条语句由前面的两条 SELECT 语句组成,之间用 UNION 关键字分隔。UNION 指示 DBMS 执行这两条 SELECT 语句,并把输出组合成一个查询结果集。
参考:
《SQL 必知必会》
[数据库] SQL 语法之进阶篇的更多相关文章
- [数据库] SQL 语法之基础篇
一.什么是 SQL ? SQL 是 Structured Query Language(结构化查询语言)的缩写,是一种专门用来与数据库沟通的语言.与其他语言(如英语或 C.C++.Java 这样的编程 ...
- MySQL的操作数据库SQL语法
MySQL的操作数据库SQL语法 顺序:操作数据库 > 操作数据库中的表 > 操作数据库中的表的数据 MySQL不区分大小写字母 1. 操作数据库 1.创建数据库 2.删除数据库 3.使用 ...
- 数据库-SQL 语法
数据库-SQL 语法 二十余年如一梦,此身虽在堪惊. 简介:数据库-SQL 语法 一.基础 模式定义了数据如何存储.存储什么样的数据以及数据如何分解等信息,数据库和表都有模式. 主键的值不允许修改,也 ...
- java核心技术第二篇之数据库SQL语法
#查询products表记录SELECT * FROM products WHERE price > 2000;-- 单行注释/* 多行注释*/#创建数据库CREATE DATABASE hei ...
- SQL手工注入进阶篇
0.前言 上一篇我们介绍了SQL手工注入的流程以及步骤,但在实际的安全问题以及CTF题目中,查询语句多种多样,而且是肯定会对用户的输入进行一个安全过滤的,而这些过滤并不一定是百分百安全的,如何利用一些 ...
- 数据库SQL语法到MySQL实操
一.基础 1.说明:创建数据库CREATE DATABASE database-name 2.说明:删除数据库drop database dbname 3.说明:备份sql server--- 创建 ...
- 数据库 SQL语法二
聚合函数 -SUM([DISTINCT] FIELDNAME) 求指定列之和,[DISTINCT]选项表示剔除重复记录 例如:SELECT SUM(age) FROM TABLE1; SELECT S ...
- 数据库 | SQL语法优化方法及实例详解
使用复合索引 如果经常执行如上查询,那么建立三个单独索引不如建立一个复合索引,因为三个单独索引通常数据库每次执行只能使用其中一个,虽然这样比不使用索引而进行全表扫描提高了很多效率,但使用复合索引因为索 ...
- MongoDB语法与现有关系型数据库SQL语法比较
MongoDB语法 MySql语法 db.test.find({'name':'foobar'}) <==> select ...
随机推荐
- dubbo(提供者、消费者)基于java的实现
1.安装好jdk.zookeeper以后可以尝试开发代码进行dubbo的学习和练习. 首先创建Dubbo的Provider配置.创建一个maven project工程. RPC框架,不希望Consum ...
- web api 记录部署IIS获取服务器地址的类型
获取服务器地址类型分多种,以下记录 1.HttpContext.Current.Server.MapPath("~/File") 返回的值为 D:\3Project\Code\Mo ...
- ASP.NET Core 3.0 使用AspectCore-Framework实现AOP
AspectCore是适用于Asp.Net Core 平台的轻量级Aop(Aspect-oriented programming)解决方案,它更好的遵循Asp.Net Core的模块化开发理念,使用A ...
- 在Angular4中使用ng2-baidu-map详解
一.引言 之前在Angular4使用过百度地图,记录一下踩过的坑 二.实现 1.安装 npm install angular2-baidu-map 2.在app.module.ts配置 ak key在 ...
- RV64I基础整数指令集
RV64I是RV32I的超集,RV32I是RV64I的子集.RV64I包括RV32I的所有40条指令,另外增加了12条RV32I中没有的指令,还有三条移位指令(slli, srli,srai)也进行小 ...
- maven 学习---Maven配置之pom文件配置包含和排除测试
本文地址:http://blog.csdn.net/wirelessqa/article/details/14057083 包含(Inclusions )默认情况下Surefire Plugin会自动 ...
- 白话SCRUM 之二:product backlog
在SCRUM方法中明确要求了3个文档: 1 product backlog 2sprint backlog 3 burn-down chart Product backlog 中列举了本项目应该实现的 ...
- Django ORM 一对多 和 多对多
一对多 在 models.py 上定义: class Province(models.Model): name = models.CharField(max_length=32) def __str_ ...
- 前台VUE的组件之间传参方式
路由传参 """ 转跳: <router-link :to="'/course/'+course.id">{{course.name}}& ...
- 网络远程唤醒 WOL Magic Packet【转】
转自:https://www.cnblogs.com/dcb3688/p/4608063.html Magic Packet Magic Packet白皮书介绍: The basic technica ...