低效的“WHERE 1=1”
加了“1=1”的过滤条件以后数据库系统就无法使用索引等查询优化策略,数据库系统将会被迫
对每行数据进行扫描(也就是全表扫描)以比较此行是否满足过滤条件,当表中数据量比较大的
时候查询速度会非常慢
HAVING 语句
数据表
CREATE TABLE `t_employee` ( `FNumber` varchar(20) NOT NULL DEFAULT '', `FName` varchar(20) DEFAULT NULL, `FAge` int(11) DEFAULT NULL, `FSalary` decimal(10,2) DEFAULT NULL, `FSubCompany` varchar(20) DEFAULT NULL, `FDepartment` varchar(20) DEFAULT NULL, PRIMARY KEY (`FNumber`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of t_employee -- ---------------------------- INSERT INTO `t_employee` VALUES ('DEV001', 'Tom', '25', '8300.00', 'BeiJing', 'Development'); INSERT INTO `t_employee` VALUES ('DEV002', 'Jerry', '28', '2300.80', 'ShenZhen', 'Development'); INSERT INTO `t_employee` VALUES ('HR001', 'Jane', '23', '2200.88', 'Beijing', 'HumanResource'); INSERT INTO `t_employee` VALUES ('HR002', 'Tina', '25', '5200.36', 'Beijing', 'HumanResource'); INSERT INTO `t_employee` VALUES ('IT001', 'Smith', '28', '3900.00', 'Beijing', 'InfoTech'); INSERT INTO `t_employee` VALUES ('IT002', null, '27', '2800.00', 'ShenZhen', 'InfoTech'); INSERT INTO `t_employee` VALUES ('SALES001', 'John', '23', '5000.00', 'Beijing', 'Sales'); INSERT INTO `t_employee` VALUES ('SALES002', 'Kerry', '28', '6200.00', 'Beijing', 'Sales'); INSERT INTO `t_employee` VALUES ('SALES003', 'Stone', '22', '1200.00', 'ShenZhen', 'Sales');
有的时候需要对部分分组进行过滤,比如只检索人数多余1个的年龄段
SELECT FAge,COUNT(*) AS CountOfThisAge FROM T_Employee GROUP BY FAge HAVING COUNT(*)>1
HAVING语句中也可以像WHERE语句一样使用复杂的过滤条件,比如下面的SQL用来检索人数为1个或者3个的年龄段,可以使用下面的SQL:(也可以使用IN操作符来实现上面的功能)
SELECT FAge,COUNT(*) AS CountOfThisAge FROM T_Employee GROUP BY FAge HAVING COUNT(*) =1 OR COUNT(*) =3 SELECT FAge,COUNT(*) AS CountOfThisAge FROM T_Employee GROUP BY FAge HAVING COUNT(*) IN (1,3)
HAVING语句能够使用的语法和WHERE几乎是一样的,需要特别注意,在HAVING语句中不能包含未分组的列名,比如下面的SQL语句是错误的:
SELECT FAge,COUNT(*) AS CountOfThisAge FROM T_Employee GROUP BY FAge HAVING FName IS NOT NULL
限制结果集行数(分页)
MYSQL
MYSQL中提供了LIMIT关键字用来限制返回的结果集,LIMIT放在SELECT语句的最后位置,语法为“LIMIT 首行行号,要返回的结果集的最大数目”。
比如下面的SQL语句将返回按照工资降序排列的从第三行开始(行号从0开始)的最多五条记录:
SELECT * FROM T_Employee ORDER BY FSalary DESC LIMIT 2,5
很显然,下面的SQL语句将返回按照工资降序排列的前五条记录:
SELECT * FROM T_Employee ORDER BY FSalary DESC LIMIT 0,5
Oracle
Oracle中支持窗口函数 ROW_NUMBER()
ROW_NUMBER() 【语法】ROW_NUMBER() OVER (PARTITION BY COL1 ORDER BY COL2) 【功能】表示根据COL1分组,在分组内部根据 COL2排序,而这个值就表示每组内部排序后的顺序编号(组内连续的唯一的) row_number() 返回的主要是“行”的信息,并没有排名 【参数】 【说明】Oracle分析函数 主要功能:用于取前几名,或者最后几名等
【示例】 表内容如下: name | seqno | description A | 1 | test A | 2 | test A | 3 | test A | 4 | test B | 1 | test B | 2 | test B | 3 | test B | 4 | test C | 1 | test C | 2 | test C | 3 | test C | 4 | test 我想有一个sql语句,搜索的结果是 A | 1 | test A | 2 | test B | 1 | test B | 2 | test C | 1 | test C | 2 | test 实现: select name,seqno,description from(select name,seqno,description,row_number() over (partition by name order by seqno) id from table_name) where id<=3;
SELECT * FROM ( SELECT ROW_NUMBER() OVER(ORDER BY FSalary DESC) row_num, FNumber,FName,FSalary,FAge FROM T_Employee ) a WHERE a.row_num>=3 AND a.row_num<=5
===============================================================================================================================================
字符串的拼接
MYSQL
在MYSQL中也可以使用加号“+”来连接两个字符串,比如下面的SQL:
SELECT '12'+'33',FAge+'1' FROM T_Employee
MYSQL会尝试将加号两端的字段值尝试转换为数字类型,如果转换失败则认为字段值为0,如 SELECT 'abc'+'123',FAge+'a' FROM T_Employee
在MYSQL中进行字符串的拼接要使用CONCAT函数,CONCAT函数支持一个或者多个参数,参数类型可以为字符串类型也可以是非字符串类型,对于非字符串类型的参数MYSQL将尝试将其转化为字符串类型,CONCAT函数会将所有参数按照参数的顺序拼接成一个字符串做为返回值。
SELECT CONCAT('工号为:',FNumber,'的员工的幸福指数:',FSalary/(FAge-21)) FROM T_Employee
MYSQL中还提供了另外一个进行字符串拼接的函数CONCAT_WS,CONCAT_WS可以在待拼接的字符串之间加入指定的分隔符,它的第一个参数值为采用的分隔符,而剩下的参数则为待拼接的字符串值
Oracle字符串拼接
Oracle中使用“||”进行字符串拼接,或者CONCAT()函数
SELECT '工号为'||FNumber||'的员工姓名为'||FName FROM T_Employee WHERE FName IS NOT NULL
SELECT CONCAT('工号:',FNumber) FROM T_Employee
============================================
联合结果集
CREATE TABLE T_TempEmployee (FIdCardNumber VARCHAR(20),FName VARCHAR(20),FAge INT ,PRIMARY KEY (FIdCardNumber)) INSERT INTO T_TempEmployee(FIdCardNumber,FName,FAge) VALUES('1234567890121','Sarani',33); INSERT INTO T_TempEmployee(FIdCardNumber,FName,FAge) VALUES('1234567890122','Tom',26); INSERT INTO T_TempEmployee(FIdCardNumber,FName,FAge) VALUES('1234567890123','Yalaha',38); INSERT INTO T_TempEmployee(FIdCardNumber,FName,FAge) VALUES('1234567890124','Tina',26); INSERT INTO T_TempEmployee(FIdCardNumber,FName,FAge) VALUES('1234567890125','Konkaya',29); INSERT INTO T_TempEmployee(FIdCardNumber,FName,FAge) VALUES('1234567890126','Fo fa' ,46) ;
有的时候我们需要组合两个完全不同的查询结果集,而这两个查询结果之间没有必然的联系,只是我们需要将他们显示在一个结果集中而已。在SQL中可以使用UNION运算符来将两个或者多个查询结果集联合为一个结果集中。
SELECT FNumber,FName,FAge FROM T_Employee UNION SELECT FIdCardNumber,FName,FAge FROM T_TempEmployee
使用UNION仍然有两个基
本的原则需要遵守:一是每个结果集必须有相同的列数;二是每个结果集的列必须类型相容。
可以这样,两者的列数就相同了:
SELECT FNumber,FName,FAge,FDepartment FROM T_Employee UNION SELECT FIdCardNumber,FName,FAge,'临时工,不属于任何一个部门' FROM T_TempEmployee
UNION ALL:默认情况下,UNION运算符合并了两个查询结果集,其中完全重复的数据行被合并为了一条。如果需要在联合结果集中返回所有的记录而不管它们是否唯一,则需要在UNION运算符后使用ALL操作符
==============================================================================
获取系统时间
MYSQL
NOW()函数 用于取得当前的 日期时间,NOW()函数还有SYSDATE()、CURRENT_TIMESTAMP 等别名
SELECT NOW(),SYSDATE(),CURRENT_TIMESTAMP
当前日期, CURDATE()函数, CURRENT_DATE 是其别名
SELECT CURDATE(),CURRENT_DATE
当前时间,时分秒, CURTIME()函数,CURTIME ()函数还有 CURRENT_TIME 等别名
SELECT CURTIME(),CURRENT_TIME
Oracle
Oracle 中没有提供取得当前日期时间的函数
SYSTIMESTAMP 的值来得到当前的时间戳
SELECT SYSTIMESTAMP FROM DUAL
到系统表 DUAL中查询 SYSDATE 的值来得到当前日期时间
select sysdate from dual
Oracle 中也没有专门提供取得当前日期、取得当前时间的函数,借助于 TO_CHAR() 函数对SYSDATE 的值进行处理
select to_char(sysdate,'YYYY-MM-DD') from dual select to_char(sysdate,'HH24:MI:SS') from dual
=================================================================================
日期增减
MYSQL DATE_ADD (date,INTERVAL expr type)
其中参数date为待计算的日期;参数expr为待进行加法运算的增量,它可以是数值类型
或者字符串类型,取决于type参数的取值;参数type则为进行加法运算的单位
比如DATE_ADD(date,INTERVAL 1 HOUR)就可以得到在日期
date基础上增加一小时后的日期时间,而DATE_ADD(date,INTERVAL 1 WEEK)就可以得到在日
期date基础上增加一周后的日期时间。下面的SQL语句用来计算每个人出生一周、两个月以
及5个季度后的日期:
SELECT FBirthDay, DATE_ADD(FBirthDay,INTERVAL 1 WEEK) as w1, DATE_ADD(FBirthDay,INTERVAL 2 MONTH) as m2, DATE_ADD(FBirthDay,INTERVAL 5 QUARTER) as q5 FROM T_Person
在MYSQL中提供了 DATE_SUB()函数用于计算指定日期前的特定时间段的日期,
其效果和在 DATE_ADD()函数中使用负数的expr参数值的效果一样,其用法也和DATE_ADD()
函数几乎相同。 下面的SQL语句用来计算每个人出生一周、两个月以及3天2小时10分钟前的
日期:
SELECT FBirthDay, DATE_SUB(FBirthDay,INTERVAL 1 WEEK) as w1, DATE_SUB(FBirthDay,INTERVAL 2 MONTH) as m2, DATE_SUB(FBirthDay, INTERVAL '3 2:10' DAY_MINUTE) as dm FROM T_Person
Oracle
Oracle中可以直接使用加号“+”来进行日期的加法运算,其计算单位为“天”,比如date+3
就表示在日期date的基础上增加三天;同理使用减号“-”则可以用来计算日期前的特定时
间段的时间,比如date+3就表示在日期date的三天前的日期。比如下面的SQL语句用于计算
每个人出生日期3天后以及10天前的日期:
SELECT FBirthDay,FBirthDay+3,FBirthDay-10 FROM T_Person
可以使用换算的方式来进行以周、小时、分钟等为单位的日期加减运算,比如下面的SQL
语句用于计算每个人出生日期2小时10分钟后以及3周后的日期:
SELECT FBirthDay, FBirthDay+(2/24+10/60/24), FBirthDay+(3*7) FROM T_Person
使用加减运算我们可以很容易的实现以周、天、小时、分钟、秒等为单位的日期的增减运
算,不过由于每个月的天数是不同的,也就是在天和月之间不存在固定的换算率,所以无法使用
加减运算实现以月为单位的计算,为此Oracle中提供了ADD_MONTHS()函数用于以月为单位的
日期增减运算,ADD_MONTHS()函数的参数格式如下:
ADD_MONTHS(date,number)
其中参数date为待计算的日期,参数number为要增加的月份数,如果number为负数则表
示进行日期的减运算。下面的SQL语句用于计算每个人的出生日期两个月后以及10个月前的日期:
SELECT FBirthDay, ADD_MONTHS(FBirthDay,2), ADD_MONTHS(FBirthDay,-10) FROM T_Person
综合使用 ADD_MONTHS()函数和加、减号运算符则可以实现更加复杂的日期增减运算,比
如下面的SQL语句用于计算每个人的出生日期两个月零10天后以及3个月零10个小时前的日期
时间:
SELECT FBirthDay, ADD_MONTHS(FBirthDay,2)+10 as bfd, ADD_MONTHS(FBirthDay,-3)-(10/24) as afd FROM T_Person
=============================================================================
计算日期差额
有时候我们需要计算两个日期的差额,比如计算“回款日”和“验收日”之间所差的天
数或者检索所有“最后一次登录日期”与当前日期的差额大于100天的用户信息。主流的数
据库系统中都提供了对计算日期差额的支持
mysql : DATEDIFF(date1,date2)
函数将返回date1与date2之间的天数差额,如果date2在date1之后返回正值,否则返回负值
比如下面的SQL语句用于计算注册日期和出生日期之间的天数差额:
SELECT FRegDay,FBirthDay, DATEDIFF(FRegDay, FBirthDay) , DATEDIFF(FBirthDay ,FRegDay) FROM T_Person
DATEDIFF()函数只能计算两个日期之间的天数差额,如果要计算两个日期的周差额等就
需要进行换算,比如下面的SQL语句用于计算注册日期和出生日期之间的周数差额:
SELECT FRegDay,FBirthDay, DATEDIFF(FRegDay, FBirthDay)/7 FROM T_Person
Oracle
在Oracle中,可以在两个日期类型的数据之间使用减号运算符“-”,其计算结果为两个
日期之间的天数差,比如执行下面的SQL语句用于计算注册日期 FRegDay和出生日期
FBirthDay之间的时间间隔:
SELECT FRegDay,FBirthDay,FRegDay-FBirthDay FROM T_Person
注意通过减号运算符“-”计算的两个日期之间的天数差是包含有小数部分的,小数部
分表示不足一天的部分,比如执行下面的SQL语句用于计算当前时刻 和出生日期FBirthDay
之间的时间间隔:
SELECT SYSDATE,FBirthDay,SYSDATE-FBirthDay FROM T_Person
可以看到天数差的小数部分是非常精确的,所以完全可以精确的表示两个日期时间值之
间差的小时、分、秒甚至毫秒部分。所以如果要计算两个日期时间值之间的小时、分、秒以
及毫秒差的话,只要进行相应的换算就可以,比如下面的SQL用来计算当前时刻 和出生日期
FBirthDay之间的时间间隔( 小时、分以及秒 ):
SELECT (SYSDATE-FBirthDay)*24,(SYSDATE-FBirthDay)*24*60, (SYSDATE-FBirthDay)*24*60*60 FROM T_Person
下面的SQL语句用来计算当前时刻 和出生日期FBirthDay之间的周间隔
SELECT SYSDATE,FBirthDay,(SYSDATE-FBirthDay)/7 FROM T_Person
可以看到计算结果含有非常精确的小数部分,不过如果对这些小数部分没有需求的话则
可以使用数值函数进行四舍五入、取最大整数等处理,比如下面的SQL用来计算当前时刻 和
出生日期FBirthDay之间的时间间隔( 小时、分以及秒 ),并且对于计算结果进行四舍五入运
算:
SELECT ROUND((SYSDATE-FBirthDay)*24),ROUND((SYSDATE-FBirthDay)*24*60), ROUND((SYSDATE-FBirthDay)*24*60*60) FROM T_Person
计算一个日期是星期几
计算一个日期是星期几是非常有用的,比如如果安排的报到日期是周末那么就向后拖延报到日期
MYSQL
MYSQL中提供了DAYNAME()函数用于计算一个日期是星期几
比如下面的SQL语句用于计算出生日期和注册日期各是星期几:
SELECT FBirthDay,DAYNAME(FBirthDay), FRegDay,DAYNAME(FRegDay) FROM T_Person
Oracle
Oracle中提供了TO_CHAR()函数用于将数据转换为字符串类型,当针对时间日期类型数
据进行转换的时候,它接受两个参数,其参数格式如下:
TO_CHAR(date,format)
其中参数date为待转换的日期,参数format为格式化字符串,数据库系统将按照这个字
符串对date进行转换,格式化字符串中可以采用如下的占位符:
可以简单的将占位符做为参数传递给TO_CHAR()函数,下面的SQL语句用于计算出生日
期的年份、月份以及周数:
SELECT FBirthDay, TO_CHAR(FBirthDay, 'YYYY') as yyyy, TO_CHAR(FBirthDay, 'MM') as mm, TO_CHAR(FBirthDay, 'MON') as mon, TO_CHAR(FBirthDay, 'WW') as ww FROM T_Person
同样还可以将占位符组合起来实现更加复杂的转换逻辑,比如下面的SQL语句用于以
“2008-08-08”这样的形式显示出生日期以及以“31-2007-02”这样的形式显示注册日期:
SELECT FBirthDay, TO_CHAR(FBirthDay, 'YYYY-MM-DD') as yyymmdd, FRegDay, TO_CHAR(FRegDay, 'DD-YYYY-MM') as ddyyyymm FROM T_Person
我们前面提到了,当用“DAY”做为参数的时候就可以将日期格式化为名字的形式表示
的星期几,比如下面的SQL语句用于计算出生日期以及注册日期各属于星期几:
SELECT FBirthDay,TO_CHAR(FBirthDay, 'DAY') as birthwk, FRegDay,TO_CHAR(FRegDay, 'DAY') as regwk FROM T_Person
===============================================================================
取得日期的指定部分
提取日期的特定部分是非常有必要的,比如检索本年的每个月的16日的销售量、检索访问用户集中的时间段
MYSQL
MYSQL中提供了一个DATE_FORMAT()函数用来将日期按照特定各是进行格式化,这个函
数的参数格式如下:
DATE_FORMAT(date,format)
这个函数用来按照特定的格式化指定的日期,其中参数date为待计算的日期值,而参数
format为格式化字符串,格式化字符串中可以采用如下的占位符:
组合使用这些占位符就可以实现非常复杂的字符串格式化逻辑,比如下面的SQL语句实
现了将出生日期 FBirthDay和注册日期FRegDay分别按照两种格式进行格式化:
SELECT FBirthDay, DATE_FORMAT(FBirthDay,'%y-%M %D %W') AS bd, FRegDay, DATE_FORMAT(FRegDay,'%Y年%m月%e日') AS rd FROM T_Person
很显然,如果只使用单独的占位符那么就可以实现提取日期特定部分的功能了,比如
DATE_FORMAT(date,'%Y')可以用来提取日期的年份部分、DATE_FORMAT(date,'%H')可以用来提
取日期的小时部分、DATE_FORMAT(date,'%M')可以用来提取日期的月份名称。下面的SQL用
于提取每个人员的出生年份、出生时是当年的第几天、出生时是当年的第几周:
SELECT FBirthDay, DATE_FORMAT(FBirthDay,'%Y') AS y, DATE_FORMAT(FBirthDay,'%j') AS d, DATE_FORMAT(FBirthDay,'%U') AS u FROM T_Person
Oracle
Oracle中使用TO_CHAR()函数格式化日期的方法,使用它就可
以提取日期的任意部分,比如下面的SQL用于提取每个人员的出生年份、出生时是当年的第
几天、出生时是当年的第几周:
SELECT FBirthDay, TO_CHAR(FBirthDay,'YYYY') AS y, TO_CHAR(FBirthDay,'DDD') AS d, TO_CHAR(FBirthDay,'WW') AS u FROM T_Person
===================================================
类型转换
在使用SQL语句的时候,我们使用的数据的类型不一定符合函数或者运算符的需要,比
如函数需要整数类型的数据而我们使用的则是一个字符串,在一些情况下数据库系统会替我
们自动将字符串类型转换为整数类型,这种转换称为隐式转换。但是在有的情况下数据库系
统不会进行隐式转换,这时就要使用类型转换函数了,这种转换称为显式转换。使用类型转
换函数不仅可以保证类型转换的正确性,而且可以提高数据处理的速度,因此应该使用显式
转换,尽量避免使用隐式转换
MYSQL
MYSQL中提供了CAST()函数和CONVERT()函数用于进行类型转换,CAST()是符合ANSI
SQL99的函数,CONVERT() 是符合ODBC标准的函数,这两个函数只是参数的调用方式略有差
异,其功能几乎相同。这两个函数的参数格式如下:
CAST(expression AS type)
CONVERT(expression,type)
参数expression为待进行类型转换的表达式,而type为转换的目标类型,type可以是下面
的任一个:
下面的SQL语句分别演示以有符号整形、无符号整形、日期类型、时间类型为目标类型
的数据转换:
SELECT CAST('-30' AS SIGNED) as sig, CONVERT ('36', UNSIGNED INTEGER) as usig, CAST('2008-08-08' AS DATE) as d, CONVERT ('08:09:10', TIME) as t
Oracle
Oracle中也有一个名称为CONVERT()的函数,不过这个函数是用来进行字符集转换的。
Oracle中不支持用做数据类型转换的CAST()和CONVERT()两个函数,它提供了针对性更强的类
型TO_CHAR()、TO_DATE()、TO_NUMBER()等函数,这些函数可以将数据显式的转换为字符串
类型、日期时间类型或者数值类型。Oracle中还提供了HEXTORAW()、RAWTOHEX()、
TO_MULTI_BYTE()、TO_SINGLE_BYTE()等函数用于存储格式的转换。下面我们将对这些函数进
行分别介绍。
1) TO_CHAR()
TO_CHAR(expression,format)
参数expression为待转换的表达式,参数format为转换后的字符串格式,
SELECT FBirthDay, TO_CHAR(FBirthDay,'YYYY-MM-DD') as c1, FWeight, TO_CHAR(FWeight,'L99D99MI') as c2, TO_CHAR(FWeight) as c3 FROM T_Person
2) TO_DATE()
TO_DATE()函数用来将字符串转换为时间类型,其参数格式如下:
TO_DATE (expression,format)
参数expression为待转换的表达式,参数format为转换格式
SELECT TO_DATE('2008-08-08 08:09:10', 'YYYY-MM-DD HH24:MI:SS') as dt1, TO_DATE('20080808 080910', 'YYYYMMDD HH24MISS') as dt2 FROM DUAL
3) TO_NUMBER()
TO_NUMBER()函数用来将字符串转换为数值类型,其参数格式如下:
TO_NUMBER (expression,format)
参数expression为待转换的表达式,参数format为转换格式
SELECT TO_NUMBER('33.33') as n1, TO_NUMBER('100.00', '9G999D99') as n2 FROM DUAL
==================================================
空值处理
COALESCE()函数
COALESCE ( expression,value1,value2……,valuen)
COALESCE()函数的第一个参数expression为待检测的表达式,而其后的参数个数不定。
COALESCE()函数将会返回包括expression在内的所有参数中的第一个非空表达式。如果
expression不为空值则返回expression;否则判断value1是否是空值,如果value1不为空值则返
回value1;否则判断value2是否是空值,如果value2不为空值则返回value3;……以此类推,
如果所有的表达式都为空值,则返回NULL。
我们将使用COALESCE()函数完成下面的功能,返回人员的“重要日期”:如果出生日期
不为空则将出生日期做为“重要日期”,如果出生日期为空则判断注册日期是否为空,如果
注册日期不为空则将注册日期做为“重要日期”,如果注册日期也为空则将“2008年8月8日”
做为“重要日期”。实现此功能的SQL语句如下:
SELECT FName,FBirthDay,FRegDay, COALESCE(FBirthDay,FRegDay,'2008-08-08') AS ImportDay FROM T_Person
COALESCE()函数的简化版
COALESCE()函数可以用来完成几乎所有的空值处理,不过在很多数据库系统中都提供了
它的简化版,这些简化版中只接受两个变量,其参数格式如下:
MYSQL:
IFNULL(expression,value)
Oracle:
NVL(expression,value)
CASE函数
用法一 ,实现简单的“等于”逻辑的判断
SELECT FName, (CASE FLevel WHEN 1 THEN 'VIP客户' WHEN 2 THEN ' 高级客户 ' WHEN 3 THEN ' 普通客户' ELSE '客户类型错误' END) as FLevelName FROM T_Customer
用法二,大于小于的判断
ELECT FName, FWeight, (CASE WHEN FWeight<40 THEN 'thin' WHEN FWeight>50 THEN 'fat' ELSE 'ok' END) as isnormal FROM T_Person
欢迎关注个人公众号一起交流学习: