xSky 实验室关注高性能计算,分布式系统/存储,大数据/机器学习/WebRTC
目录
  • 首页
  • 技术相关
  • 原创作品
  • 人工智能/机器学习
  • 系统与架构
  • 数据库/数据分析
  • 分布式系统/存储
  • 服务端开发
  • WEBRTC研究
  • 开发调试
  • 网络与安全
  • 常用工具
  • 杂七杂八

使用MySQL进行数据统计

2019-01-14 07:42:50

mysql是常用数据库,这篇文章主要给大家介绍了关于mysql中数据统计技巧,对MySQL数据统计相关使用技巧进行归纳总结。

一、计数统计

数据统计里最常用使用的就是计数了,一般我们使用COUNT()函数。如果使用 COUNT(*),那么计算出来的结果将是查询所选取到的行数。
如果查询语句没有带 WHERE 子句,那么它会查询所有行,因此,COUNT(*) 计算出来的结果就是表的行数。

1.下面这个查询可以显示出 student 表里有多少行:

mysql> SELECT COUNT(*) FROM student;

2.下面这个查询可以查看 student 表里有多少不同的性别:

mysql> SELECT COUNT(DISTINCT sex) FROM student;

3.如果查询语句带有 WHERE 子句,那么COUNT(*) 计算出来的结果就是该语句匹配到了多少行。下面这个查询可以显示出 student 表中有多少 Female:
        

mysql> SELECT COUNT(*) FROM student WHERE sex='F';

4.COUNT(*) 函数会统计所有被查询到的行数,与之相对的是,COUNT(列名)只会统计所有非NULL值的数目。下面这个查询展示了这两个函数的区别:
        

mysql> SELECT COUNT(DISTINCT sex) ,COUNT(name),COUNT(student_id) FROM student;

5. 还可以使用 GROUP BY 语句分别统计男、女学生的人数:
        

mysql> SELECT sex , COUNT(*) FROM student GROUP BY sex;

6. 如果不是想用LIMIT子句来限制查询结果中的记录条数,而是想把COUNT() 的某些特定值找出来,那么需要用到HAVING子句。该子句与WHERE相类似,
    他们都可以用来设定输出行所必须满足的查询条件。与WHERE子句不同的是,它可以引用像COUNT()那样的汇总函数输出的结果。
    下面的查询显示了 student 表中个数超过3个的那种性别:
        

mysql> SELECT sex,COUNT(*) AS count 
FROM student 
GROUP BY sex HAVING count> 3 ORDER BY count DESC;

    一般情况下,带有HAVING子句的查询语句,特别适合于查找在某个数据列里重复出现的值。也可以用于查找不重复出现的值,此时使用 HAVING count=1 即可。

 7. count(expr):根据表达式统计数据

mysql> select * from TT;

+------+------------+
| UNIT | DATE       |
+------+------------+
| a    | 2018-04-03 |
| a    | 2017-12-12 |
| b    | 2018-01-01 |
| b    | 2018-04-03 |
| c    | 2016-06-06 |
| d    | 2018-03-03 |
+------+------------+
6 rows in set (0.00 sec)

mysql> select UNIT as '单位',
    ->     COUNT(TO_DAYS(DATE)=TO_DAYS(NOW()) or null) as '今日统计',
    ->     COUNT(YEAR(DATE)=YEAR(NOW()) or null) as '今年统计'
    -> from v_jjd
    -> group by JJDW;
+------+----------+----------+
| 单位  | 今日统计  | 今年统计  |
+------+----------+----------+
| a    |        1 |        1 |
| b    |        1 |        2 |
| c    |        0 |        0 |
| d    |        0 |        1 |
+------+----------+----------+
4 rows in set (0.00 sec)

二、按时间统计

order订单表,样例如下:

CREATE TABLE `yyd_order` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `order_nid` varchar(50) NOT NULL,
  `status` varchar(50) NOT NULL DEFAULT '0',
  `money` decimal(20,2) NOT NULL DEFAULT '0.00',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `userid` (`user_id`),
  KEY `createtime` (`create_time`),
  KEY `updatetime` (`update_time`)
) ENGINE=InnoDB;

1. 按天统计进单量,date_format

SELECT DATE_FORMAT(t.`create_time`, '%Y-%m-%d') t_date, COUNT(1) t_count FROM t_order t
WHERE t.`create_time` > '2018-05-11' GROUP BY DATE_FORMAT(t.`create_time`, '%Y-%m-%d');

2. 按小时统计进单量

SELECT DATE_FORMAT(t.`create_time`, '%Y-%m-%d %H') t_hour, COUNT(1) t_count FROM t_order t
WHERE t.`create_time` > '2018-05-11' GROUP BY DATE_FORMAT(t.`create_time`, '%Y-%m-%d %H');

3. 同比昨天进单量对比,order by h, date

SELECT DATE_FORMAT(t.`create_time`, '%Y-%m-%d %H') t_date, COUNT(1) t_count FROM yyd_order t
WHERE t.`create_time` > '2018-05-11' GROUP BY DATE_FORMAT(t.`create_time`, '%Y-%m-%d %H')
ORDER BY DATE_FORMAT(t.`create_time`, '%H'), DATE_FORMAT(t.`create_time`, '%Y-%m-%d %H');


4. 环比上周同小时进单,date in ,order by

SELECT DATE_FORMAT(t.`create_time`, '%Y-%m-%d %H') t_date, COUNT(1) t_count FROM yyd_order t WHERE
DATE_FORMAT(t.`create_time`,'%Y-%m-%d') IN ('2018-05-03','2018-05-11') GROUP BY DATE_FORMAT(t.`create_time`, '%Y-%m-%d %H')
ORDER BY DATE_FORMAT(t.`create_time`, '%H'),DATE_FORMAT(t.`create_time`, '%Y-%m-%d %H');


5. 按照remark字段中的返回值进行统计,group by remark like ...

SELECT DATE_FORMAT(t.`create_time`, '%Y-%m-%d') t_date, COUNT(1) t_count, SUBSTRING_INDEX(SUBSTRING_INDEX(t.`msg`, '{', -1), '}', 1) t_rsp_msg FROM
cmoo_tab t WHERE t.`create_time` > '2018-05-17' AND t.`rsp_msg` LIKE '%nextProcessCode%C9000%'
GROUP BY DATE_FORMAT(t.`create_time`, '%Y-%m-%d'),SUBSTRING_INDEX(SUBSTRING_INDEX(t.`rsp_msg`, '{', -1), '}', 1);


6. 统计每小时的各金额的区间数统计,sum if 1 0,各自统计

SELECT DATE_FORMAT(t.create_time,'%Y-%m-%d') t_date, SUM(IF(t.`amount`>0 AND t.`amount`<1000, 1, 0)) t_0_1000, SUM(IF(t.`amount`>1000 AND t.`amount`<5000, 1, 0)) t_1_5000,
  SUM(IF(t.`amount`>5000, 1, 0)) t_5000m FROM mobp2p.`yyd_order` t WHERE t.`create_time` > '2018-05-11' GROUP BY DATE_FORMAT(t.`create_time`, '%Y-%m-%d');

7. 按半小时统计进单量,floor h / 30,同理10分钟,20分钟

SELECT CONCAT(DATE_FORMAT(create_time, '%Y-%m-%d %H:' ),IF(FLOOR(DATE_FORMAT(create_time, '%i') / 30 ) = 0, '00','30')) AS time_scope, COUNT(*)
FROM yyd_order WHERE create_time>'2018-05-11' GROUP BY time_scope ORDER BY DATE_FORMAT(create_time, '%H:%i'), DATE_FORMAT(create_time, '%Y-%m-%d') DESC ;

 

 

三、分组SELECT

SELECT select_expr [, select_expr ...]
[FROM table_references
[PARTITION partition_list]
[WHERE where_condition]
[GROUP BY {col_name | expr | position}
[ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_condition]
[ORDER BY {col_name | expr | position}
[ASC | DESC], ...]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]

分组SELECT的基本格式:
  select [聚合函数] 字段名 from 表名
    [where 查询条件]
    [group by 字段名]
    [having 过滤条件]

1、group by子句
  根据给定列或者表达式的每一个不同的值将表中的行分成不同的组,使用组函数返回每一组的统计信息

规则:

  ①出现在SELECT子句中的单独的列,必须出现在GROUP BY子句中作为分组列
  ②分组列可以不出现在SELECT子句中
  ③分组列可出现在SELECT子句中的一个复合表达式中
  ④如果GROUP BY后面是一个复合表达式,那么在SELECT子句中,它必须整体作为一个表达式的一部分才能使用。

1)指定一个列进行分组

mysql> select salary,count(*) from salary_tab
-> where salary>=2000
-> group by salary;
+---------+----------+
| salary | count(*) |
+---------+----------+
| 2000.00 | 1 |
| 3000.00 | 1 |
+---------+----------+

2)指定多个分组列,‘大组中再分小组’

mysql> select userid,count(salary) from salary_tab
-> where salary>=2000
-> group by salary,userid;
+--------+---------------+
| userid | count(salary) |
+--------+---------------+
| 2 | 1 |
| 3 | 1 |
+--------+---------------+

3)根据表达式分组

mysql> select year(payment_date),count(*)
-> from PENALTIES
-> group by year(payment_date);
+--------------------+----------+
| year(payment_date) | count(*) |
+--------------------+----------+
| 1980 | 3 |
| 1981 | 1 |
| 1982 | 1 |
| 1983 | 1 |
| 1984 | 2 |
+--------------------+----------+
5 rows in set (0.00 sec)

4)带有排序的分组:如果分组列和排序列相同,则可以合并group by和order by子句

mysql> select teamno,count(*)
-> from MATCHES
-> group by teamno
-> order by teamno desc;
+--------+----------+
| teamno | count(*) |
+--------+----------+
| 2 | 5 |
| 1 | 8 |
+--------+----------+
2 rows in set (0.00 sec)
mysql> select teamno,count(*)
-> from MATCHES
-> group by teamno desc;  #可以把desc(或者asc)包含到group by子句中简化
+--------+----------+
| teamno | count(*) |
+--------+----------+
| 2 | 5 |
| 1 | 8 |
+--------+----------+
2 rows in set (0.00 sec)

对于分组聚合注意:
  通过select在返回集字段中,这些字段要么就要包含在group by语句后面,作为分组的依据,要么就要被包含在聚合函数中。
我们可以将group by操作想象成如下的一个过程:首先系统根据select语句得到一个结果集,然后根据分组字段,将具有相同分组字段的记录归并成了一条记录。
这个时候剩下的那些不存在与group by语句后面作为分组依据的字段就很有可能出现多个值,但是目前一种分组情况只有一条记录,一个数据格是无法放入多个数值的,
所以这个时候就需要通过一定的处理将这些多值的列转化成单值,然后将其放在对应的数据格中,那么完成这个步骤的就是前面讲到的聚合函数,这也就是为什么这些函数叫聚合函数了。

 

2、GROUP_CONCAT()函数
函数的值等于属于一个组的指定列的所有值,以逗号隔开,并且以字符串表示。

例1:对于每个球队,得到其编号和所有球员的编号

mysql> select teamno,group_concat(playerno)
-> from MATCHES
-> group by teamno;
+--------+------------------------+
| teamno | group_concat(playerno) |
+--------+------------------------+
| 1 | 6,6,6,44,83,2,57,8 |
| 2 | 27,104,112,112,8 |
+--------+------------------------+
2 rows in set (0.01 sec)


如果没有group by子句,group_concat返回一列的所有值

例2:得到所有的罚款编号列表

mysql> select group_concat(paymentno)
-> from PENALTIES;
+-------------------------+
| group_concat(paymentno) |
+-------------------------+
| 1,2,3,4,5,6,7,8 |
+-------------------------+
1 row in set (0.00 sec)

3、with rollup子句:用来要求在一条group by子句中进行多个不同的分组

用的比较少点,但是有时可以根据具体的需求使用

  如果有子句GROUP BY E1,E2,E3,E4 WITH ROLLUP

  那么将分别执行以下分组:[E1,E2,E3,E4]、[E1,E2,E3]、[E1,E2]、[E1]、[]

注意:[ ]表示所有行都分在一组中

示例:按照球员的性别和居住城市,统计球员的总数;统计每个性别球员的总数;统计所有球员的总数

mysql> select sex,town,count(*)
-> from PLAYERS
-> group by sex,town with rollup;
+-----+-----------+----------+
| sex | town | count(*) |
+-----+-----------+----------+
| F | Eltham | 2 |
| F | Inglewood | 1 |
| F | Midhurst | 1 |
| F | Plymouth | 1 |
| F | NULL | 5 |
| M | Douglas | 1 |
| M | Inglewood | 1 |
| M | Stratford | 7 |
| M | NULL | 9 |
| NULL | NULL | 14 |
+-----+-----------+----------+
10 rows in set (0.00 sec)

4、HAVING子句:对分组结果进行过滤

注意:
  不能使用WHERE子句对分组后的结果进行过滤
  不能在WHERE子句中使用组函数,仅用于过滤行

mysql> select playerno
-> from PENALTIES
-> where count(*)>1
-> group by playerno;
ERROR 1111 (HY000): Invalid use of group function


因为WHERE子句比GROUP BY先执行,而组函数必须在分完组之后才执行,且分完组后必须使用having子句进行结果集的过滤。

基本语法:

SELECT select_expr [, select_expr ...]
FROM table_name
[WHERE where_condition]
[GROUP BY {col_name | expr} [ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_condition]

!!!having子语句与where子语句区别:
  where子句在分组前对记录进行过滤;
  having子句在分组后对记录进行过滤

mysql> select salary,count(*) from salary_tab
-> where salary>=2000
-> group by salary
-> having count(*)>=0;
+---------+----------+
| salary | count(*) |
+---------+----------+
| 2000.00 | 1 |
| 3000.00 | 1 |
+---------+----------+

1)HAVING可以单独使用而不和GROUP BY配合,如果只有HAVING子句而没有GROUP BY,表中所有的行分为一组
2)HAVING子句中可以使用组函数
3)HAVING子句中的列,要么出现在一个组函数中,要么出现在GROUP BY子句中(否则出错)

mysql> select town,count(*)
-> from PLAYERS
-> group by town
-> having birth_date>'1970-01-01';

ERROR 1054 (42S22): Unknown column 'birth_date' in 'having clause'

 

mysql> select town,count(*)
-> from PLAYERS
-> group by town
-> having town in ('Eltham','Midhurst');
+----------+----------+
| town | count(*) |
+----------+----------+
| Eltham | 2 |
| Midhurst | 1 |
+----------+----------+
2 rows in set (0.00 sec)

 

四、集合查询操作

union用于把两个或者多个select查询的结果集合并成一个

SELECT ...
UNION [ALL | DISTINCT]
SELECT ...
[UNION [ALL | DISTINCT]
SELECT ...]

默认情况下,UNION = UNION DISTINCT

  ①进行合并的两个查询,其SELECT列表必须在数量和对应列的数据类型上保持一致;
  ②默认会去掉两个查询结果集中的重复行;默认结果集不排序;
  ③最终结果集的列名来自于第一个查询的SELECT列表
UNION ALL不去掉结果集中重复的行
注:联合查询结果使用第一个select语句中的字段名

 

五、区间分组统计

对区间进行分组并且统计落在各区间内的数据量, 主要使用 etl 和 INTERVAL 函数实现。

elt函数格式:

ELT(N,str1,str2,str3,…)

如果N为1, 则结果为str1, 如果N小于1或大于参数个数,返回NULL

interval函数格式:

INTERVAL(N,N1,N2,N3,…)

如果N<N1返回0, 如果N<N2, 返回1, 如果N是null. 返回-1

示例如下:

mysql> select * from k1;
+------+------+
| id | yb |
+------+------+
| 1 | 100 |
| 2 | 11 |
| 3 | 5 |
| 4 | 501 |
| 5 | 1501 |
| 6 | 1 |
+------+------+


现在要进行统计,小于100的,100~500的,500~1000的,1000以上的,这各个区间的id数

利用interval划出4个区间

再利用elt函数将4个区间分别返回一个列名

select elt(interval(d.yb,0, 100, 500, 1000), 'less100', '100to500', '500to1000', 'more1000') as yb_level,
count(d.id) as cnt from k1 d
group by elt(interval(d.yb, 0, 100, 500, 1000), 'less100', '100to500', '500to1000', 'more1000K');
+-----------+-----+
| yb_level | cnt |
+-----------+-----+
| 100to500 | 1 |
| 500to1000 | 1 |
| less100 | 3 |
| more1000 | 1 |
+-----------+-----+
4 rows in set (0.00 sec)

 

六、窗口函数

尝试了一下MySQL 8.0的部分新特性。

  如果用过MSSQL或者是Oracle中的窗口函数(Oracle中叫分析函数),然后再使用MySQL 8.0之前的时候,就知道需要在使用窗口函数处理逻辑的痛苦了,虽然纯SQL也能实现类似于窗口函数的功能,但是这种SQL在可读性和以及使用方式上大打折扣,看起来写起了都比较难受。

  在MSSQL和Oracle以及PostgreSQL都已经完整支持窗口函数的情况下,MySQL 8.0中也加入了窗口函数的功能,这一点实实在在方便了sql的编码,可以说是MySQL8.0的亮点之一。

  对于窗口函数,比如row_number(),rank(),dense_rank(),NTILE(),PERCENT_RANK()等等,在MSSQL和Oracle以及PostgreSQL,使用的语法和表达的逻辑,基本上完全一致。

  这一点,几个数据库厂商做的还是比较统一的,如果熟悉任何一种关系数据中的窗口函数(分析函数),在MySQL 8.0之后就放心的用吧。

  通过一个case来体验一下窗口函数的方便性,熟悉MSSQL或者Oracle或者PostgreSQL的老司机就不用看了。

  测试case,简单模拟一个订单表,字段分别是订单号,用户编号,金额,创建时间

drop table  if exists order_info

create table order_info
(
    order_id int primary key,
    user_no varchar(10),
    amount int,
    create_date datetime
);

insert into order_info values (1,'u0001',100,'2018-1-1');
insert into order_info values (2,'u0001',300,'2018-1-2');
insert into order_info values (3,'u0001',300,'2018-1-2');
insert into order_info values (4,'u0001',800,'2018-1-10');
insert into order_info values (5,'u0001',900,'2018-1-20');

insert into order_info values (6,'u0002',500,'2018-1-5');
insert into order_info values (7,'u0002',600,'2018-1-6');
insert into order_info values (8,'u0002',300,'2018-1-10');
insert into order_info values (9,'u0002',800,'2018-1-16');
insert into order_info values (10,'u0002',800,'2018-1-22');

要求sql查询求每个用户的最新的一个订单。

传统的方式,尽量格式化的好读一点的情况下,说实话,这句sql咋一看有点莫名其妙,不知所以。

SELECT * FROM 
(
    SELECT 
   IF(@y=a.user_no, @x:=@x+1, @x:=1) X ,
    IF(@y=a.user_no, @y, @y:=a.user_no) Y,
    a.*
    FROM order_info a, (SELECT @x:=0, @y:=NULL) b
    ORDER BY a.user_no, a.create_date desc
) a
WHERE X <= 1;

如下是执行结果,当然执行结果是可以满足需求的。

  如果采用新的窗口函数的方法,
  就是使用row_number()over(partition by user_no order by create_date desc) as row_num 给原始记录编一个号,
  然后取第一个编号的数据,自然就是“用户的最新的一条订单”,实现逻辑上清晰了很多,代码也简洁,可读了很多。

select * from 
(
    select row_number()over(partition by user_no order by create_date desc) as row_num,
    order_id,user_no,amount,create_date
    from order_info
)t where row_num=1;

  需要注意的是,MySQL中的使用窗口函数的时候,是不允许使用*的,必须显式指定每一个字段。

 

 row_number()

  (分组)排序编号,正如上面的例子, row_number()over(partition by user_no order by create_date desc) as row_num,按照用户分组,按照create_date排序,对已有数据生成一个编号。
  当然也可以不分组,对整体进行排序。任何一个窗口函数,都可以分组统计或者不分组统计(也即可以不要partition by ***都可以,看你的需求了)

  

rank()

  类似于 row_number(),也是排序功能,但是rank()有什么不一样?新的事物的出现必然是为了解决潜在的问题。
  如果再往测试表中写入一条数据:insert into order_info values (11,'u0002',800,'2018-1-22');
  对于测试表中的U002用户来说,有两条create_date完全一样的数据(假设有这样的数据),那么在row_number()编号的时候,这两条数据却被编了两个不同的号
  理论上讲,这两条的数据的排名是并列最新的。因此rank()就是为了解决这个问题的,也即:排序条件一样的情况下,其编号也一样。

  

dense_rank()

  dense_rank()的出现是为了解决rank()编号存在的问题的,
  rank()编号的时候存在跳号的问题,如果有两个并列第1,那么下一个名次的编号就是3,结果就是没有编号为2的数据。
  如果不想跳号,可以使用dense_rank()替代。

  

avg,sum等聚合函数在窗口函数中的的增强

  可以在聚合函数中使用窗口功能,比如sum(amount)over(partition by user_no order by create_date) as sum_amont,达到一个累积计算sum的功能
  这种需求在没有窗口函数的情况下,用纯sql写起来,也够蛋疼的了,就不举例了。

  

NTILE(N) 将数据按照某些排序分成N组

  举个简单的例子,按照分数线的倒序排列,将学生成绩分成上中下3组,可以得到哪个程序数据上中下三个组中哪一部分,就可以使用NTILE(3) 来实现。这种需求倒是用的不是非常多。
  如下还是使用上面的表,按照时间将user_no = 'u0002'的订单按照时间的纬度,划分为3组,看每一行数据数据哪一组。

  

first_value(column_name) and last_value(column_name)

  first_value和last_value基本上见名知意了,就是取某一组数据,按照某种方式排序的,最早的和最新的某一个字段的值。
  看结果体会一下。

nth_value(column_name,n)

  从排序的第n行还是返回nth_value字段中的值,这个函数用的不多,要表达的这种逻辑,说实话,很难用语言表达出来,看个例子体会一下就行。

  n = 3

  n = 4

cume_dist

  在某种排序条件下,小于等于当前行值的行数/总行数,得到的是数据在某一个纬度的分布百分比情况。
  比如如下示例
  第1行数据的日期(create_date)是2018-01-05 00:00:00,小于等于2018-01-05 00:00:00的数据是1行,计算方式是:1/6 = 0.166666666
  第2行数据的日期(create_date)是2018-01-06 00:00:00,小于等于2018-01-06 00:00:00的数据是2行,计算方式是:2/6 = 0.333333333
  依次类推
  第4行数据的日期(create_date)是2018-01-16 00:00:00,小于等于2018-01-16 00:00:00的数据是4行,计算方式是:4/6 = 0.6666666666
  第一行数据的0.6666666666 意味着,小于第四行日期(create_date)的数据占了符合条件数据的66.66666666666%  
       

percent_rank()

  同样是数据分布的计算方式,只不过算法变成了:当前RANK值-1/总行数-1 。
  具体算法不细说,这个实际中用的也不多。

lag以及lead

  lag(column,n)获取当前数据行按照某种排序规则的上n行数据的某个字段,lead(column,n)获取当前数据行按照某种排序规则的下n行数据的某个字段,
  确实很拗口。
  举个实际例子,按照时间排序,获取当前订单的上一笔订单发生时间和下一笔订单发生时间,(可以计算订单的时间上的间隔度或者说买买买的频繁程度)

select order_id,
        user_no,
        amount,
        create_date,
      lag(create_date,1) over (partition by user_no order by create_date asc) 'last_transaction_time',
      lead(create_date,1) over (partition by user_no order by create_date asc) 'next_transaction_time'from order_info ;

  

七、公用表表达式(CTE )

CTE有两种用法,非递归的CTE和递归的CTE。
  非递归的CTE可以用来增加代码的可读性,增加逻辑的结构化表达。
  平时我们比较痛恨一句sql几十行甚至上上百行,根本不知道其要表达什么,难以理解,对于这种SQL,可以使用CTE分段解决,
  比如逻辑块A做成一个CTE,逻辑块B做成一个CTE,然后在逻辑块A和逻辑块B的基础上继续进行查询,这样与直接一句代码实现整个查询,逻辑上就变得相对清晰直观。
  举个简单的例子,当然这里也不足以说明问题,比如还是第一个需求,查询每个用户的最新一条订单
  第一步是对用户的订单按照时间排序编号,做成一个CTE,第二步对上面的CTE查询,取行号等于1的数据。

  另外一种是递归的CTE,递归的话,应用的场景也比较多,比如查询大部门下的子部门,每一个子部门下面的子部门等等,就需要使用递归的方式。
  这里不做细节演示,仅演示一种递归的用法,用递归的方式生成连续日期。

  

  当然递归不会无限下去,不同的数据库有不同的递归限制,MySQL 8.0中默认限制的最大递归次数是1000。
  超过最大低估次数会报错:Recursive query aborted after 1001 iterations. Try increasing @@cte_max_recursion_depth to a larger value.
  由参数@@cte_max_recursion_depth决定。

  

  关于CTE的限制,跟其他数据库并无太大差异,比如CTE内部的查询结果都要有字段名称,不允许连续对一个CTE多次查询等等,相信熟悉CTE的老司机都很清楚。

窗口函数和CTE的增加,简化了SQL代码的编写和逻辑的实现,并不是说没有这些新的特性,这些功能都无法实现,只是新特性的增加,可以用更优雅和可读性的方式来写SQL。

By:xSky | 数据库/数据分析 |

  • 分类目录

    • 技术相关 (35)
    • 原创作品 (13)
    • 人工智能/机器学习 (6)
    • 系统与架构 (9)
    • 数据库/数据分析 (11)
    • 分布式系统/存储 (4)
    • 服务端开发 (7)
    • WEBRTC研究 (7)
    • 开发调试 (7)
    • 网络与安全 (9)
    • 常用工具 (9)
    • 杂七杂八 (6)
  • 最新文章

    • 嵌入式分析型数据库(DuckDB)
    • WSL从C盘迁移到其他盘区
    • 赵何娟:中国AI追随之路的五大误区,我们至少落后十年
    • zap  发送日志到 websocket
    • QUIC(隐藏的)超能力
    • MYSQL 生成日期/时间序列总结
    • Linux bash终端设置代理(proxy)访问
    • centos 下 yum安装python3
    • 使用SQL查询Milvus 向量数据库
    • 浅谈 MySQL 新的身份验证插件 caching_sha2_password
    • Milvus v2.2.1 开源向量搜索引擎使用教程
    • 部署了一个SRS的demo
    • Dockerfile 详解
    • Docker常用命令
    • Tus文件上传协议
    • 编译运行Milvus
    • MinIO 快速入门
    • ESP32
    • Prometheus监控报警系统搭建
    • go语言JSON字典模拟
    • go语言的sql解析器
    • Grafana配置数据源,自定义查询语法
    • TDengine + Telegraf + Grafana
    • gRPC-Gateway 返回JSON数据int64类型被转为string类型问题
    • LLAMA模型试玩
    • 语音识别的一些开源项目整理
    • 使用MYSQL8进行统计分析
    • 记录FFmpeg抽帧、合流、转码、加水印等操作
    • 移动网络弱网处理研究
    • 翻译:使用 Semgrep 进行热点代码评审
  • 链接

    • xSky的Blog
    • 我的Github
    • 实时监控图表
    • 预印本
    • xRedis 在线文档
    • xSkyProxy
    • xChart 数据在线测试
    • 我的电子书
    • xChart 数据可视化系统
    • 树莓派技术圈
    • WebRTC开发者社区
  • 开源项目

    • xReis C++的redis客户端库
    • xBlog-C++ 博客程序
    • xSkyProxy-新型MySQL代理网关
    • 数据可视化平台- xChart
    • xhttpcache 高速数据缓存服务
    • xMonitor-图形监测工具
    • 网址收集

Powered By xBlog

Copyright 2010~2024 0xsky.com All Rights Reserved.