什么是SQL函数?为什么使用SQL函数可能会带来问题?

在SQL语言中,包括内置函数和自定义函数。内置函数是系统内置的通用函数,而自定义函数是我们根据自己的需要编写的,下面讲解的是SQL的内置函数。

你需要从以下几个方面掌握SQL函数:

  1. 什么是SQL函数?
  2. 内置的SQL函数都包括哪些?
  3. 如何使用SQL函数对一个数据表进行操作,比如针对一个王者荣耀的英雄数据库,我们可以使用这些函数完成哪些操作?
  4. 什么情况下使用SQL函数?为什么使用SQL函数有时候会带来问题?

什么是SQL函数

当我们学习编程语言的时候,也会遇到函数。函数的作用是什么呢?它可以经常把我们经常使用的代码封装起来,需要的时候直接调用即可。这样既提高了代码效率,又提高了可维护性。

SQL中的函数一般是在数据上执行的,可以很方便的转换和处理数据。一般来说,当我们从数据表中检索出数据之后,就可以进一步对这些数据进行操作,得到更有意义的结果,比如返回指定条件的函数,或者求某个字段的平均值等。

常用的SQL函数

SQL提供了一些常用的内置函数,当然你也可以自己定义SQL函数。SQL的内置函数对于不同的数据库软件来说具有一定的通用性,我们可以把内置函数分成四类:

  1. 算术函数
  2. 字符串函数
  3. 日期函数
  4. 转换函数

这4类函数分别代表了算术处理、字符串处理、日期处理、数据类型转换,它们是SQL函数常用的划分形式,可以思考下,为什么是这4个维度?

函数是对提取出来的数据进行操作,那么数据表中字段类型的定义有哪几种呢?

我们经常会保存一些数值,不论是整数类型,还是浮点类型,实际上对应得就是数值类型。同样我们也会保存一些文本内容,可能是人名,也可能是某个说明,对应得就是字符串类型。此外我们还需要保存时间,也就是日期类型。那么针对数值、字符串和日期类型得数据,我们可以对它们分别进行算术函数、字符串函数以及日期函数得操作。如果想要完成不同类型数据之间得转换,就可以使用转换函数。

算术函数

算术函数,顾名思义就是对数值类型的字段进行算术运算。常用的算术函数及含义如下表所示:

函数名 定义
ABS() 取绝对值
MOD() 取余
ROUND() 四舍五入为指定的小数位数,需要有两个参数,分别为字段名称、小数位数

这里举一些简单的例子:

SELECT ABS(-2);					// 运行结果为2
SELECT MOD(101,3);			// 运行结果为2
SELECT ROUND(37.25,1);	// 运行结果37.3

字符串函数

常用的字符串函数操作包含了字符串拼接,大小写转换,求长度以及字符串替换和截取等。具体的函数名称及含义如下表所示:

函数名 定义
CONCAT() 将多个字符串拼接起来
LENGTH() 计算字段的长度,一个汉字算三个字符,一个数字或字母算一个字符
CHAR_LENGTH() 计算字段的长度,汉字、数字、字母都算一个字符
LOWER ()
UPPER() 将字符串中的字符转化为大写
REPLACE() 替换函数,有3个参数,分别为:要替换的表达式或字段名、想要查找的被替换字符串、替换成哪个字符串
SUBSTRING() 截取字符串,有3个参数,分别为:待截取的表达式或字段名、开始截取的位置、想要截取的字符串长度

这里同样有一些简单的例子,可以自己运行下:

SELECT CONCAT ('abc', 123); 	// 运行结果为abc123
SELECT LENGTH('你好');			// 运行结果为6
SELECT CHAR_LENGTH('你好');  // 运行结果为2
SELECT LOWER('ABC'); 	// 运行结果为abc
SELECT UPPER('abc');		// 运行结果为ABC
SELECT REPLACE('fabc','abc',123);  // 运行结果为f123
SELECT SUBSTRING('fabc',1,3);  // 运行结果为fab

日期函数

日期函数是对数据表中的日期进行处理,常用的函数包括:

函数名 定义
CURRENT_DATE() 系统当前日期
CURRENT_TIME() 系统当前时间,没有具体的日期
CURRENT_TIMESTAMP() 系统当前时间,包括具体的日期+时间
EXTRACT() 抽取具体的年、月、日
DATE() 返回时间的日期部分
YEAR() 返回时间的年份部分
MONTH() 返回时间的月份部分
DAY() 返回时间的天数部分
HOUR() 返回时间的小时部分
MINUTE() 返回时间的分钟部分
SECOND() 返回时间的秒部分

下面是一些简单的例子,可以自己运行下:

SELECT CURRENT_DATE(); 		// 运行结果为2019-04-03
SELECT CURRENT_TIME();         // 运行结果为21:26:34
SELECT CURRENT_TIMESTAMP(); // 运行结果为2019-04-03 21:26:34
SELECT EXTRACT(YEAR FROM '2019-04-03'); // 运行结果为2019
SELECT DATE('2019-04-01 12:00:05');  // 运行结果为2019-04-01

这里需要注意的是,DATE日期格式必须是yyy-mm-dd形式。如果要进行日期比较,就要使用DATE函数,不要直接使用日期与字符串进行比较,会在后面的例子中讲具体的原因。

转化函数

转换函数可以转换数据之间的类型,常用的函数如下所示:

函数名 定义
CAST() 数据类型转换,参数是一个表达式,表达式通过AS关键词分割了2个参数,分别是原始数据和目标数据类型
COALESCE() 返回第一个非空数值

这两个函数不像其他函数,看一眼函数名就知道代表什么,如何使用。下面举了这个两个函数的例子,需要自己运行下:

SELECT CAST(123.123 AS INT);						//  运行结果会报错
SELECT CAST(123.123 AS DECIMAL(8,2));		// 运行结果为123.12
SELECT COALESCE(null,1,2);							// 运行结果为1

CAST函数在转换数据类型的时候,不会四舍五入,如果原数值有小数,那么转换为整数类型的时候就会报错。不过可以指定转化的小数类型,在MySQL和SQL Server中,可以用DECIMAL(a,b)来指定,其中a代表整数部分和小数部分加起来的最大位数,b代表小数位数,比如DECIMAL(8,2)代表的是精度为8位(整数加小数位最多为8位),小数位数为2位的数据类型。所以SELECT CAST(123.123 AS DECIMAL(8,2))转换结果为123.12。

用SQL函数对王者荣耀英雄数据做处理

可以使用Navicat可视化数据管理工具将.sql文件导入到数据库中。数据表为heros,然后使用此次所学的SQL函数,对这个英雄数据表进行处理。

首先显示英雄以及他的物攻成长,对应字段为attack_growth。我们让这个字段精确到小数点后一位,需要使用的是算术函数里的ROUND函数。

SQL:SELECT name, ROUND(attack_growth,1) AS attack_growth FROM heros ;

代码中,ROUND(attack_growth,1)中的attack_growth代表想要处理的数据,“1”代表四舍五入的位数,也就是我们这里需要精确到的位数。

假设我们想要显示英雄最大生命值的最大值,就需要用到MAX函数。在数据中,“最大生命值”对应的列数为hp_max,在代码中的格式为MAX(hp_max)。

SQL:SELECT MAX(hp_max) FROM heros;

假设我们想要知道最大生命值最大的是哪个英雄,以及对应的数值,就需要分成两个步骤来处理:首先找到英雄的最大生命值的最大值,即SELECT MAX(hp_max) FROM heros,然后再筛选最大生命值等于这个最大值的英雄,如下所示:

SQL:SELECT name,hp_max FROM heros WHERE hp_max = ( SELECT MAX(hp_max) FROM heros);

假设我们想要显示英雄的名字,以及他们名字的字数,需要用到CHAR_LENGTH函数。

SQL:SELECT name , CHAR_LENGTH(name) AS name_len FROM heros;

假设想要提取英雄上线日期(对应字段birthdate)的年份,只显示有上线日期的英雄即可(有些英雄没有上线日期的数据,不需要显示),这里我们需要使用EXTRACT函数,提取某一时间元素。所以我们需要筛选上线日期不为空的英雄,即WHERE birthdate IS NOT NULL,然后再显示他们的名字和上线日期的年份,即:

SQL:SELECT name,EXTRACT(YEAR FROM birthdate) AS birthdate_year FROM heros WHERE birthdate IS NOT NULL;
或者:
SQL:SELECT name,YEAR(birthdate) AS birthdate FROM heros WHERE birthdate IS NOT NULL ORDER BY birthdate;

假设我们需要找出在2016年10月1日之后上线的所有英雄。这里我们可以采用DATE函数来判断birthdate的日期是否大于2016-10-01,即WHERE DATE(birthdate) > ‘2016-10-01’,然后再显示符合要求的全部字段信息,即:

SQL:SELECT name,YEAR(birthdate) AS birthdate FROM heros WHERE DATE(birthdate) > '2016-10-01';

需要注意的是下面这种写法是不安全的:

SQL:SELECT * FROM heros WHERE birthdate > '2016-10-01';

因为很多时候无法确认birthdate的数据类型是字符串,还是datetime类型,如果你想对日期部分进行比较,那么使用DATE(birthdate)来进行比较是更安全的。

假设我们需要知道在2016年10月1日之后上线英雄的平均最大生命值、平均最大法力和最高物攻最大值。同样我们需要先筛选日期条件,即WHERE DATE(birthdate) > ‘2016-10-01’,然后再选择AVG(hp_max),AVG(mp_max),MAX(attack_max)字段进行显示。

SQL:SELECT AVG(hp_max),AVG(mp_max),MAX(attack_max) FROM heros WHERE DATE(birthdate) > '2016-10-01';

总结:

函数对于一门语言的重要性毋庸置疑,我们在写Python代码的时候,会自己编写函数,也会使用Python内置的函数。在SQL中,使用函数的时候需要格外留意。不过如果工程量不大,使用的是同一个DBMS的话,还是可以使用函数简化操作的,这样也能提高代码效率。只是在系统集成,或者在多个DBMS同时存在的情况下,使用函数的时候就需要谨慎一些。

比如CONCAT()是字符串拼接函数,在MySQL和Oracle中都有这个函数,但是在这两个DBMS中作用却不一样,CONCAT函数在MySQL中可以连接多个字符串,而在Oracle中CONCAT函数只能连接两个字符串,如果要连接多个字符串就需要用(||)连接字符来解决。

什么是SQL函数?为什么使用SQL函数可能会带来问题?_第1张图片

练习:

  1. 计算英雄的最大生命平均值:
SELECT AVG(hp_max) FROM heros;
  1. 显示出所有在2017年之前上线的英雄,如果没有统计上线日期则不显示:
SELECT name FROM heros WHERE birthdate IS NOT NULL AND YEAR(birthdate) < '2017';

你可能感兴趣的:(数据库)