MySQL 使用自定义变量进行查询优化


Posted in MySQL onMay 14, 2021

优化排序查询

自定义变量的一个重要特性是你可以同时将该变量的数学计算后的结果再赋值给该变量,类似于我们的 i = i + 1这种方式。下面是一个用于计算数据表行号的例子:

SET @rownum := 0;
SELECT actor_id, @rownum := @rownum + 1 AS rownum
FROM sakila.actor LIMIT 3;

 

actor_id rownum
1 1
2 2
3 3

得到的结果也许看起来没什么意义,这是因为主键是从1自增的,因此行号和主键值是一样的。但是,这种方式可以用于做排序。例如需要查询饰演电影数量最多的前10名演员,通常的做法是像下面这样写:

SELECT actor_id, COUNT(*) as cnt
FROM sakila.film_actor
GROUP BY actor_id
ORDER BY cnt DESC
LIMIT 10;

得到的结果也许看起来没什么意义,这是因为主键是从1自增的,因此行号和主键值是一样的。但是,这种方式可以用于做排序。例如需要查询饰演电影数量最多的前10名演员,通常的做法是像下面这样写:

SELECT actor_id, COUNT(*) as cnt
FROM sakila.film_actor
GROUP BY actor_id
ORDER BY cnt DESC
LIMIT 10;

如果我们要获得相应的排名值的话,则可以引入变量来完成:

SET @curr_cnt := 0, @prev_cnt := 0, @rank := 0;
SELECT actor_id,
	@curr_cnt := cnt AS cnt,
  @rank 		:= IF(@prev_cnt <> @curr_cnt, @rank+1, @rank) as rank,
  @prev_cnt	:= @curr_cnt AS dummy
FROM (
  SELECT actor_id, COUNT(*) AS cnt
  FROM sakila.film_actor
	GROUP BY actor_id
	ORDER BY cnt DESC
	LIMIT 10
) as der;

这里是将饰演电影的数量赋值给了 curr_cnt 变量,使用了prev_cnt 存储前一个演员的参演数量。排名从第一名开始的,如果后面的演员的数量和前一个演员的数量不同,则排名要往下(+1),如果相同则和前一个演员的排名相同。通过这种方式可以直接从查询结果中得到演员的排名,而不需要再从数据库查询做二次处理(当然也可以通过程序代码实现)。

避免重复获取刚刚修改的数据行

如果想在更新数据行的时候再重新获取数据行的信息,往往需要再读取一次数据库。这是因为 MySQL 不像 PostgreSQL 的 UPDATE RETURNING 功能可以同时返回更新后的数据行,而只是返回更新影响的行数。但是,我们可以通过自定义变量完成这样的操作。例如,获取刚刚被修改过更新时间的行,不使用自定义变量的话需要做一次额外的查询:

UPDATE tb1 SET lastUpdated = NOW() WHERE id = 1;
SELECT lastUpdated FROM tb1 WHERE id = 1;

而使用自定义变量的时候可以避免这种情况:

UPDATE tb1 SET lastUpdated = NOW() WHERE id = 1 AND @now  := NOW();
SELECT @now;

虽然还是有一个查询操作,但是后面的查询操作不再需要访问数据库了。

懒加载的联合查询

假设我们需要写一个联合查询完成如下任务:在联合的分支上查找匹配的数据行,如果找到了就跳过其他分支。y这种情况发生在需要从热区数据或低频访问数据中查找(比如近期订单和历史订单)。这是下面针对用户查询的一个普通的 SQL:

SELECT id FROM users WHERE  id = 123
UNION ALL
SELECT id FROM users_archived WHERE id = 123;

这个查询会先从当前正在使用的用户表查询 id 为123的用户,然后 在从已归档的用户表找同样 id 的用户。但是,这种写法比较低效,即便是在 users 表找到了想要找的用户,还是需要从users_archived 这个表再找一次,而实际用户 id 为123的只会存在其中的一张表中或两张表的数据是一样的。通过懒加载的联合查询,可以避免这种情况——只有在第一个分支没有找到数据时才进行第二个分支的查询。因此可以使用 MySQL 的 GREATEST 方法来作为查询结果的容器以避免多返回数据列。

SELECT GREATEST(@found := -1, id) AS id, users.name, 'users' as which_tb1
FROM users WHERE id = 123
UNION ALL
	SELECT id, users_archived.name, 'users_archived'
  FROM users_archived WHERE id = 123 AND @found IS NULL
UNION ALL
	SELECT 1, '', 'reset' FROM DUAL WHERE ( @found := NULL) IS NOT NULL;

上述的查询如果第一行有结果,则@found 不会被赋值,因而是 NULL,从而执行第二次查询。而第三次的 UNION 实际没什么效果,只是为了将@found恢复到 NULL 值,以便这段 SQL 可以重复执行。另一个验证的方法是对同一张表进行这样的操作,可以发现实际只会返回一行数据或不返回数据(查询不到数据时)。

SELECT GREATEST(@found := -1, `id`) AS `id`, `infocenter_city`.`name`, 'city' as which_tb1 
FROM `infocenter_city` WHERE `id` = 460100 
UNION ALL 
	SELECT `id`, `infocenter_city`.`name`, 'infocenter_city' 
	FROM `infocenter_city` WHERE id = 460100 AND @found IS NULL 
UNION ALL 
	SELECT 1, '', 'reset' FROM DUAL WHERE ( @found := NULL) IS NOT NULL

以上就是MySQL 使用自定义变量进行查询优化的详细内容,更多关于MySQL 用自定义变量进行查询优化的资料请关注三水点靠木其它相关文章!

MySQL 相关文章推荐
详解MySQL的半同步
Apr 22 MySQL
教你解决往mysql数据库中存入汉字报错的方法
May 06 MySQL
mysql升级到5.7时,wordpress导数据报错1067的问题
May 27 MySQL
MySQL系列之六 用户与授权
Jul 02 MySQL
MySQL图形化管理工具Navicat安装步骤
Dec 04 MySQL
一条 SQL 语句执行过程
Mar 17 MySQL
MySQL限制查询和数据排序介绍
Mar 25 MySQL
一次Mysql update sql不当引起的生产故障记录
Apr 01 MySQL
MySql重置root密码 --skip-grant-tables
Apr 11 MySQL
MySQL 字符集 character
May 04 MySQL
Mysql开启外网访问
May 15 MySQL
MySQL添加索引特点及优化问题
Jul 23 MySQL
MySQL 逻辑备份与恢复测试的相关总结
May 14 #MySQL
MySQL 可扩展设计的基本原则
May 14 #MySQL
MySQL主从搭建(多主一从)的实现思路与步骤
May 13 #MySQL
MySQL如何构建数据表索引
May 13 #MySQL
MySQL 自定义变量的概念及特点
May 13 #MySQL
为什么mysql字段要使用NOT NULL
MySQL表字段时间设置默认值
May 13 #MySQL
You might like
关于php程序报date()警告的处理(date_default_timezone_set)
2013/10/22 PHP
laravel框架中间件 except 和 only 的用法示例
2019/07/12 PHP
通过PHP的Wrapper无缝迁移原有项目到新服务的实现方法
2020/04/02 PHP
javascript学习笔记(九)javascript中的原型(prototype)及原型链的继承方式
2011/04/12 Javascript
JavaScript原型继承之基础机制分析
2011/08/26 Javascript
JavaScript 参数中的数组展开 [译]
2012/09/21 Javascript
js读取注册表的键值示例
2013/09/25 Javascript
jquery属性选择器not has怎么写 行悬停高亮显示
2013/11/13 Javascript
了不起的node.js读书笔记之node.js中的特性
2014/12/22 Javascript
自己动手手写jQuery插件总结
2015/01/20 Javascript
javascript实现禁止复制网页内容汇总
2015/12/30 Javascript
jQuery使用serialize()表单序列化时出现中文乱码问题的解决办法
2016/07/27 Javascript
JavaScript简单获取系统当前时间完整示例
2016/08/02 Javascript
JavaScript实现星级评分
2017/01/12 Javascript
jQuery实现贪吃蛇小游戏(附源码下载)
2017/03/04 Javascript
JavaScript数组和对象的复制
2017/03/21 Javascript
详解angularjs获取元素以及angular.element()用法
2017/07/25 Javascript
javaScript之split与join的区别(详解)
2017/11/08 Javascript
js嵌套的数组扁平化:将多维数组变成一维数组以及push()与concat()区别的讲解
2019/01/19 Javascript
node.JS事件机制与events事件模块的使用方法详解
2020/02/06 Javascript
[01:11:10]2014 DOTA2华西杯精英邀请赛 5 24 iG VS VG加赛
2014/05/26 DOTA
[00:17]DOTA2荣耀之路5:It’s a disastah!
2018/05/28 DOTA
python生成二维码的实例详解
2017/10/29 Python
详解PyCharm配置Anaconda的艰难心路历程
2018/08/13 Python
PyQt5图形界面播放音乐的实例
2019/06/17 Python
Python脚本操作Excel实现批量替换功能
2019/11/20 Python
Keras模型转成tensorflow的.pb操作
2020/07/06 Python
编写html5时调试发现脚本php等网页js、css等失效
2013/12/31 HTML / CSS
HTML5超文本标记语言的实现方法
2020/09/24 HTML / CSS
迪卡侬(Decathlon)加拿大官网:源自法国的运动专业超市
2020/11/22 全球购物
文明寄语大全
2014/04/11 职场文书
我爱我校演讲稿
2014/05/21 职场文书
银行党的群众路线教育实践活动对照检查材料
2014/09/25 职场文书
2014年会计主管工作总结
2014/12/20 职场文书
学生会宣传部竞选稿
2015/11/21 职场文书
小学二年级语文教学反思
2016/03/03 职场文书