SQL Fundamentals: 子查询 || 分析函数

子查询(基础)

1、认识子查询

2、WHERE子句中使用子查询

3、在HAVING子句中使用子查询

4、在FROM子句中使用子查询

5、在SELECT子句中使用子查询

6、WITH子句

子查询(进阶)

7、分析函数

8、行列转换

9、设置数据层次

七、分析函数

  • 分析函数语法;
  • 分窗操作的;
  • 使用分析函数可以进行更为复杂的查询报表显示。
  • 在分析函数中可以使用若干统计函数.

传统SQL的问题

虽然利用SQL之中提供的各种查询命令可以完成大部分的查询要求,但是还有许多功能是无法实现的,例如:

  • 计算运行总量:逐一累加当前行与其之前行的每行记录数据;
  • 查找当前行数据占总数据的百分比;
  • 分区显示:按照不同的部门或职位进行排列、统计;
  • 计算流动数据行的平均值等。

分析函数的基本语法

基本语法:

函数名称([参数 , ....]) OVER (

PARTITION BY 子句 字段 , …

[ORDER BY 子句 字段 , … [ASC | DESC] [NULLS FIRST | NULLS LAST]

[WINDOWING 子句]) ;

语法组成:


函数名称


类似于统计函数(COUNT()、SUM()等),但是在此时有了更多的函数支持;


OVER子句


为分析函数指明一个查询结果集,此语句在SELECT子句之中使用;


PARTITION BY子句


将一个简单的结果集分为N组(或称为分区),而后按照不同的组对数据进行统计;


ORDER BY 子句


明确指明数据在每个组内的排列顺序,分析函数的结果与排列顺序有关;

NULLS FIRST | NULLS LAST:表示返回数据行中包含NULL值是出现在排序序列前还是尾;


NULLS FIRST


NULLS FIRST表示在进行排序前,出现null值的数据行排列在最前面,


NULLS LAST


NULLS LAST则表示出现的null值数据行排列在最后面。

使用NULLS LAST

SELECT deptno , ename, sal , comm ,

RANK() OVER (ORDER BY comm DESC NULLS LAST) rk ,

SUM(sal) OVER (ORDER BY comm DESC NULLS LAST) SUM

FROM emp ;


WINDOWING 子句(代名词)


给出在定义变化的固定的数据窗口方法,分析函数将对此数据进行操作。

组合顺序:

在分析函数之中存在有三种子句:PARTITION BY、ORDER BY、WINDOWING,而这三种子句的组合顺序有如下几种:


组合1


函数名称([参数 ,…]) OVER(PARTITION BY 子句 , ORDER BY 子句 , WINDOWING子句);


组合2


函数名称([参数 ,…]) OVER(PARTITION BY 子句 , ORDER BY 子句);


组合3


函数名称([参数 ,…]) OVER(PARTITION BY 子句);


组合4


函数名称([参数 ,…]) OVER(ORDER BY 子句 , WINDOWING子句);


组合5


函数名称([参数 ,…]) OVER(ORDER BY 子句);


组合6


函数名称([参数 ,…]) OVER();

基本查询语句中是不能出现字段和统计函数同时出现的.(如下语法是错误的)

SELECT deptno , ename, sal ,

SUM(sal)

FROM emp ;

1、PARTITION子句和ORDER BY子句的使用


使用PARTITION子句

按照部门范畴进行统计;每行数据之后都会有统计的结果出现.

同一部门的雇员的SUM(sal)的值相同,并且值等于同一部门雇员sal的相加.


SELECT deptno , ename, sal ,

SUM(sal) OVER (PARTITION BY deptno) sum

FROM emp ;


不使用PARTITION进行分区,直接利用OVER子句操作——如果没有分区就会把所有的数据当成一个区,然后进行统计.


SELECT deptno , ename, sal ,

SUM(sal) OVER () sum

FROM emp ;


通过PARTITION设置多个分区字段——分析部门编号和职位进行分区

这里的范畴:先按照部门分区,再按照职位分区,返回的结果是:每个部门中职位的总工资


SELECT deptno , ename, sal , job ,

SUM(sal) OVER (PARTITION BY deptno , job) sum

FROM emp ;


观察ORDER BY子句

按照部门编号分区,按照工资降序排列(两个数据相同并列为1,后面一个数据为3)


SELECT deptno , ename, sal ,

RANK() OVER (PARTITION BY deptno ORDER BY sal DESC) rk

FROM emp ;


设置多个排序字段(sal和hiredate)


SELECT deptno , ename, sal , hiredate ,

RANK() OVER (PARTITION BY deptno ORDER BY sal , hiredate DESC) rk

FROM emp ;


直接利用ORDER BY排序所有数据——如果不写分区操作,就表示所有的数据进行排序

先将所有数据变为一组,而后按照姓名进行排序;

排序后,第一个数据的sum(sal)值为其sal本身,第二个数据的sum(sal)值为第一个数据和第二个数据的总和,以此类推.


SELECT deptno , ename, sal , hiredate ,

SUM(sal) OVER (ORDER BY ename DESC) SUM

FROM emp ;

2、WINDOWING子句

分窗子句主要是用于定义一个变化或固定的数据窗口方法,主要用于定义分析函数在操作行的集合,分窗子句有两种实现方式:


值域窗(RANGE WINDOW),逻辑偏移


当前分区之中当前行的前N行到当前行的记录集;


行窗(ROWS WINDOW),物理偏移


以排序的结果顺序计算偏移当前行的起始行记录集。

而如果要想指定RANGE(值域窗)或ROWS(行窗)的偏移量,则可以采用如下的几种排序列:


1


RANGE | ROWS 数字 PRECEDING;


2


RANGE | ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW;


3


RANGE | ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING;

以上的几种排列之中包含的概念如下:


PRECEDING


主要是设置一个偏移量,这个偏移量可以是用户设置的数字,或者是其他标记;


BETWEEN … AND


设置一个偏移量的操作范围;


UNBOUNDED PRECEDING


不限制偏移量大小;


CURRENT ROW


表示当前行;


FOLLOWING


如果不写此语句表示使用上N行与当前行指定数据比较,如果编写此语句,表示当前行与下N行数据比较;

例子:

RANGE子句:RANGE子句设置的是一个逻辑的偏移量


在sal上设置偏移量(PRECEDING)

按照向上N行的记录偏移


SELECT deptno , ename, sal ,

SUM(sal) OVER (PARTITION BY deptno ORDER BY sal RANGE 300 PRECEDING) sum

FROM emp ;


设置偏移量为300,采用下匹配方式处理

向下匹配方式,显示结果向下N行的记录偏移


SELECT deptno , ename, sal ,

SUM(sal) OVER (PARTITION BY deptno ORDER BY sal RANGE BETWEEN 0 PRECEDING AND 300 FOLLOWING) sum

FROM emp ;


匹配当前行数据


SELECT deptno , ename, sal ,

SUM(sal) OVER (PARTITION BY deptno ORDER BY sal RANGE BETWEEN 0 PRECEDING AND CURRENT ROW) sum

FROM emp ;


使用UNBOUNDED不设置边界

现在的结果是在一个区域内进行逐行的操作.


SELECT deptno , ename, sal ,

SUM(sal) OVER (PARTITION BY deptno ORDER BY sal RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) sum

FROM emp ;

ROWS子句:ROWS子句设置的是一个物理的偏移量


设置2行物理偏移

按照部门编号分组,而后采用当前行和前2行数据进行计算.


SELECT deptno , ename, sal ,

SUM(sal) OVER (PARTITION BY deptno ORDER BY sal ROWS 2 PRECEDING) sum

FROM emp ;


设置查询行范围

此时和按照部门分区进行求和道理相同.


SELECT deptno , ename, sal ,

SUM(sal) OVER (PARTITION BY deptno ORDER BY sal ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) sum

FROM emp ;

分析函数:

1、数据统计函数

2、等级函数

3、报表函数

1、数据统计函数


函数名称


描述


SUM([DISTINCT | ALL] 表达式)


计算分区(分组)中的数据累加和


MIN([DISTINCT | ALL] 表达式)


查找分区(分组)中的最小值


MAX([DISTINCT | ALL] 表达式)


查找分区(分组)中的最大值


AVG([DISTINCT | ALL] 表达式)


计算分区(分组)中的数据平均值


COUNT(* | [DISTINCT | ALL] 表达式)


计算分区(分组)中的数据量


查询雇员编号是7369的雇员姓名、职位、基本工资、部门编号、部门的人数、平均工资、最高工资、最低工资、总工资


SELECT *

FROM (

SELECT empno,ename,job,sal,deptno ,

COUNT(empno) OVER (PARTITION BY deptno) count ,

ROUND(AVG(sal) OVER (PARTITION BY deptno)) avg ,

SUM(sal) OVER (PARTITION BY deptno) sum ,

MAX(sal) OVER (PARTITION BY deptno) max ,

MIN(sal) OVER (PARTITION BY deptno) min

FROM emp ) temp

WHERE temp.empno=7369 ;

法二:

WITH t AS(

SELECT empno,ename,job,sal,deptno ,

COUNT(empno) OVER (PARTITION BY deptno) count ,

ROUND(AVG(sal) OVER (PARTITION BY deptno)) avg ,

SUM(sal) OVER (PARTITION BY deptno) sum ,

MAX(sal) OVER (PARTITION BY deptno) max ,

MIN(sal) OVER (PARTITION BY deptno) min

FROM emp )

SELECT * FROM t

WHERE t.empno=7369 ;


查询每个雇员的编号、姓名、基本工资、所在部门的名称、部门位置以及此部门的平均工资、最高和最低工资


确定所需要的数据表:dept表:部门信息,名称,位置

emp表:雇员姓名,统计信息

确定已知的关联字段:

雇员和部门:emp.deptno=dept.deptno

步骤一:进行多表关联

步骤二:加入统计信息(部门的平均工资、最高和最低工资:使用分析函数,进行分窗)


SELECT e.empno , e.ename , e.sal , d.dname , d.loc ,

ROUND(AVG(sal) OVER (PARTITION BY e.deptno ORDER BY sal

RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)) avg_salary ,

MAX(sal) OVER (PARTITION BY e.deptno ORDER BY sal

RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) max_salary ,

MIN(sal) OVER (PARTITION BY e.deptno ORDER BY sal

RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) min_salary

FROM emp e,dept d

WHERE e.deptno=d.deptno ;

2、等级函数

为数据进行排序编号


函数名称


描述


RANK()


根据ORDER BY子句的排序字段,从分区(分组)查询每一行数据,按照排序生成序号,会出现相同序号,跳号


DENSE_RANK()


根据ORDER BY子句的排序字段,从分区(分组)查询每一行数据,按照排序生成序号,不跳号


FIRST


取出DENSE_RANK返回集合中第一行数据


LAST


取出DENSE_RANK返回集合中最后一行数据


FIRST_VALUE(列)


返回分区(分组)中的第一个值


LAST_VALUE(列)


返回分区(分组)中的最后一个值


LAG(列名称 [, 行数字] [, 默认值])


访问分区(分组)中指定前N行的记录,如果没有则返回默认值


LEAD(列名称 [, 行数字] [, 默认值])


访问分区(分组)中指定后N行的记录,如果没有则返回默认值


ROW_NUMBER()


返回每组中的行号


RANK()和DENSE_RANK()函数


SELECT deptno,ename,sal,

RANK() OVER (PARTITION BY deptno ORDER BY sal) rank_result ,

DENSE_RANK() OVER (PARTITION BY deptno ORDER BY sal) dense_rank_result

FROM emp ;


ROW_NUMBER()函数

针对所有数据,ROW_NUMBER()自动生成了行号,但是在每个分区里面,也按照部门生成行号


SELECT deptno,ename,sal,

ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY sal) row_result_deptno ,

ROW_NUMBER() OVER (ORDER BY sal) row_result_all

FROM emp ;

    KEEP语句

    • 保留满足条件的数据,在使用DENSE_RANK()函数确定的集合后,通过FIRST或LAST取得集合中的数据.

    分组函数 ()

    KEEP

    (DENSE_RANK FIRST | LAST ORDER BY 表达式 [ASC | DESC] [NULLS [FIRST | LAST]] , ...)

    [OVER () 分区查询] ;


查询每个部门的最高及最低工资


SELECT deptno,

MAX(sal) KEEP (DENSE_RANK FIRST ORDER BY sal DESC) max_salary ,

MIN(sal) KEEP (DENSE_RANK LAST ORDER BY sal DESC) min_salary

FROM emp

GROUP BY deptno ;


FIRST_VALUE()与LAST_VALUE()函数


SELECT deptno , empno , ename , sal ,

FIRST_VALUE(sal) OVER (PARTITION BY deptno ORDER BY sal

RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) first_result ,

LAST_VALUE(sal) OVER (PARTITION BY deptno ORDER BY sal

RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) last_result

FROM emp

WHERE deptno=10 ;


LAG()与LEAD()函数

相邻记录的比较


SELECT deptno , empno , ename , sal ,

LAG(sal,2,0) OVER (PARTITION BY deptno ORDER BY sal ) lag_result ,

LEAD(sal,2,0) OVER (PARTITION BY deptno ORDER BY sal ) lead_result

FROM emp

WHERE deptno=20 ;

3、报表函数


函数名称


描述


CUME_DIST()


计算一行在分区(分组)中的相对位置,如果分区有5条记录,记录会按照1、0.8、0.6、0.4、0.2的方式进行划分


NTILE(数字)


将一个分区(分组)分为“表达式”的散列表示

针对数据分区中的有序结果集进行划分


RATIO_TO_REPORT(表达式)


该函数计算expression/(sum(expression))的值,它给出相对于总数的百分比


验证CUME_DIST()函数


SELECT deptno,ename,sal ,

CUME_DIST() OVER (PARTITION BY deptno ORDER BY sal) cume

FROM emp

WHERE deptno IN (10,20) ;


使用NTILE()函数


SELECT deptno , sal ,

SUM(sal) OVER (PARTITION BY deptno ORDER BY sal) sum_result ,

NTILE(3) OVER (PARTITION BY deptno ORDER BY sal) ntile_result_a ,

NTILE(6) OVER (PARTITION BY deptno ORDER BY sal) ntile_result_b

FROM emp ;


RATIO_TO_REPORT


计算各部门工资所占的总工资比率

SELECT deptno ,SUM(sal) ,

ROUND(RATIO_TO_REPORT(SUM(sal)) OVER () ,5) rate ,

ROUND(RATIO_TO_REPORT(SUM(sal)) OVER () ,5) * 100 || ‘%‘ precent

FROM emp

GROUP BY deptno;

时间: 2024-07-29 05:18:28

SQL Fundamentals: 子查询 || 分析函数的相关文章

SQL Fundamentals: 子查询 || 行列转换(PIVOT,UNPIVOT,DECODE),设置数据层次(LEVEL...CONNECT BY)

SQL Fundamentals || Oracle SQL语言 子查询(基础) 1.认识子查询 2.WHERE子句中使用子查询 3.在HAVING子句中使用子查询 4.在FROM子句中使用子查询 5.在SELECT子句中使用子查询 6.WITH子句 子查询(进阶) 7.分析函数 8.行列转换 9.设置数据层次 八.行列转换 pivot和unpivot函数是Oracle 11g增加的新函数,利用此函数可以实现行列转换操作 按照原始方式实现,使用通用函数中的DECODE()函数 列字段的处理 SQ

SQL用子查询结果更新多个字段

作者:iamlasong 要求:表格的内容需要变更,变更的内容放在一个临时表中,用SQL语句更新正式表中多个字段. 如果更新一个字段,直接用字段名=子查询就可以了,多个字段更新,将字段在括号中并列写出即可,如下: update tb_jg t set t.jgfl = 'sd', (     t.zj_code, t.zj_mc) = (select a.zj_code, a.zj_mc from song_temp a where a.zj_code = t.zj_code) where ex

SQL优化-子查询&case&limit

load 导数据.notesdxtdb 数据库    total_time  475.60秒. 监控服务:仓颉 select t_.*, a.name acquirer_name,m.merchant_name, am.merchant_name acq_merchant_name,                   ag.name agency_name            from              (            select t.* ,               

SQL 基础--> 子查询

--========================= --SQL 基础--> 子查询 --========================= 一.子查询 子查询就是位于SELECT.UPDATE.或DELETE语句中内部的查询 二.子查询的分类 单行子查询 返回零行或一行 多行子查询 返回一行或多行 多列子查询 返回多列 相关子查询 引用外部SQL语句中的一列或多列 嵌套子查询 位于其它子查询中的查询 三.子查询语法 SQL> SELECT select_list FROM table WH

SQL Server子查询填充DataSet时报500内部错误的解决办法

运行环境为Visual Studio 2010,数据库为SQL Server 2008. 执行下面SQL语句 SELECT SubsiteId, SubsiteTitle, count(CollectionID) CollectionNumber,count(LName) PlantNumber FROM (SELECT DISTINCT SubsiteId, SubsiteTitle, CollectionID, LName, CName FROM Cumplag_Garden_Plants

ThinkPHP3.2 SQL alias 子查询

SELECT info_key, info_value, info_status, edit_time FROM (SELECT * FROM `detail` WHERE login = '[email protected]' ORDER BY edit_time DESC  ) AS aaa GROUP BY info_key 希望通过Thinkphp实现基于alias的子查询,终于参考下面这个文章实现了. http://www.thinkphp.cn/update/122.html 如下是

SQL 数据库 子查询及示例

子查询,又叫做嵌套查询. 将一个查询语句做为一个结果集供其他SQL语句使用,就像使用普通的表一样,被当作结果集的查询语句被称为子查询. 子查询有两种类型: 一种是只返回一个单值的子查询,这时它可以用在一个单值可以使用的地方,这时子查询可以看作是一个拥有返回值的函数: 另外一种是返回一列值的子查询,这时子查询可以看作是一个在内存中临时存在的数据表. 示例: --创建一个数据库,建立一个部门表格和部门人员表格 Create database gongs --创建一个gongs的数据库 go use

SQL随着子查询结果更新多个字段

笔者:iamlasong 要求:表格内容需要改变,在临时表中内容的变化,使用SQL官方声明更新表若干领域. 假设更新一个字段,直接用字段名=子查询就能够了,多个字段更新,将字段在括号里并列写出就可以,例如以下: update tb_jg t set t.jgfl = 'sd', (     t.zj_code, t.zj_mc) = (select a.zj_code, a.zj_mc from song_temp a where a.zj_code = t.zj_code) where exi

基本sql语句--子查询

子查询有三种:where型子查询,from型子查询,exist子查询. where型子查询 where子查询:指把内层的查询结果当成外层查询的比较条件. 例句:查询出最新的商品:select good_id,good_name from good where good_id = (select max(good_id) from good);--此句为子查询 select good_id,good_name from good order by good_id DESC limit 1; fro