用户自定义变量是一个很容易被遗忘的MySQL特性,但是用的好,发挥其潜力,在很多场景都可以写出非常高效的查询语句。

一. 实现一个按照actorid排序的列

 mysql> set @rownum :=0;
Query OK, 0 rows affected (0.00 sec) mysql> select actor_id ,@rownum :=@rownum + 1 as rownum
-> from sakila.actor limit 3;
+----------+--------+
| actor_id | rownum |
+----------+--------+
| 58 | 1 |
| 92 | 2 |
| 182 | 3 |
+----------+--------+
3 rows in set (0.00 sec)

二. 扩展一下,现在需要获取演过最多电影的前十位,针对数量作一个排名,如果数量一样,则排名相同

 mysql> set @curr_cnt :=0 ,@pre_cnt :=0 ,@rank :=0;
Query OK, 0 rows affected (0.00 sec) mysql> select actor_id,
-> @prev_cnt :=@curr_cnt as dummy,
-> @curr_cnt := cnt as cnt,
-> @rank := IF(@prev_cnt <> @curr_cnt,@rank+1,@rank) as rank
-> FROM(
-> SELECT actor_id ,count(*) as cnt
-> FROM sakila.film_actor
-> GROUP BY actor_id
-> ORDER BY cnt DESC
-> LIMIT 10
-> )as der;
+----------+-------+-----+------+
| actor_id | dummy | cnt | rank |
+----------+-------+-----+------+
| 107 | 0 | 42 | 1 |
| 102 | 42 | 41 | 2 |
| 198 | 41 | 40 | 3 |
| 181 | 40 | 39 | 4 |
| 23 | 39 | 37 | 5 |
| 81 | 37 | 36 | 6 |
| 158 | 36 | 35 | 7 |
| 144 | 35 | 35 | 7 |
| 37 | 35 | 35 | 7 |
| 106 | 35 | 35 | 7 |
+----------+-------+-----+------+
10 rows in set (0.00 sec)

三. 避免重复查询刚更新的数据

如果想要高效的更新一条记录的时间戳 ,又想返回更新的数据

 mysql> create table t2 (id int,lastUpdated datetime);
Query OK, 0 rows affected (0.03 sec) mysql> insert into t2 (id ,lastupdated)values(1,sysdate());
Query OK, 1 row affected (0.02 sec) mysql> select * from t2;
+------+---------------------+
| id | lastUpdated |
+------+---------------------+
| 1 | 2017-07-24 16:03:34 |
+------+---------------------+
1 row in set (0.01 sec) mysql> update t2 set lastUpdated=NOW() WHERE id =1 and @now :=Now();
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0 mysql> select @now, sysdate();
+---------------------+---------------------+
| @now | sysdate() |
+---------------------+---------------------+
| 2017-07-24 16:05:42 | 2017-07-24 16:06:06 |
+---------------------+---------------------+
1 row in set (0.00 sec)

四. 统计更新和插入的数量
使用 INSERT ON DUPLICATE KEY UPDATE 时,查询插入成功的条数,冲突的条数

 mysql> set @x :=0;
Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO t3(c1,c2) values(1,2),(1,3),(2,2)
-> ON DUPLICATE KEY UPDATE
-> c2=VALUES(c2)+(0*(@x:=@x+1));
Query OK, 4 rows affected (0.01 sec)
Records: 3 Duplicates: 1 Warnings: 0 mysql> select @x;
+------+
| @x |
+------+
| 1 |
+------+
1 row in set (0.00 sec) mysql> select * from t3;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 3 |
| 2 | 2 |
+----+------+
2 rows in set (0.00 sec)

五. 确定取值的顺序
想要获取sakila.actor中的一个结果

错误的查询一:
下面的查询看起来好像只返回一个结果,实际呢:

 mysql> set @row_num :=0;
Query OK, 0 rows affected (0.00 sec) mysql> SELECT actor_id,@row_num :=@row_num+1 AS cnt
-> FROM sakila.actor
-> WHERE @row_num <=1
-> ;
+----------+------+
| actor_id | cnt |
+----------+------+
| 58 | 1 |
| 92 | 2 |
+----------+------+
2 rows in set (0.00 sec) 看一下执行计划:
+----+-------------+-------+-------+---------------+---------------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------------------+---------+------+------+--------------------------+
| 1 | SIMPLE | actor | index | NULL | idx_actor_last_name | 137 | NULL | 200 | Using where; Using index |
+----+-------------+-------+-------+---------------+---------------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

这是因为where 和 select 是在 查询的不同阶段执行的造成的。

错误的查询二:
如果加上按照 first_name 排序呢 :

 mysql> set @row_num :=0;
Query OK, 0 rows affected (0.00 sec) mysql> SELECT actor_id,@row_num :=@row_num+1 AS cnt
-> FROM sakila.actor
-> WHERE @row_num <=1
-> order by first_name;
+----------+------+
| actor_id | cnt |
+----------+------+
| 71 | 1 |
| 132 | 2 |
| 165 | 3 |
| 173 | 4 |
| 125 | 5 |
| 146 | 6 |
| 29 | 7 |
| 65 | 8 |
| 144 | 9 |
| 76 | 10 |
| 49 | 11 |
| 34 | 12 |
| 190 | 13 |
| 196 | 14 |
| 83 | 15 |
.. ...
返回了所有行,再看下查询计划: +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
| 1 | SIMPLE | actor | ALL | NULL | NULL | NULL | NULL | 200 | Using where; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
1 row in set (0.00 sec)

可以看出原因是 Using where 是在排序操作之前取值的,所以输出了全部的行。

解决这个问题的方法是:让变量的赋值和取值发生在执行查询的统一阶段:

正确的查询:

 mysql> set @row_num :=0;
Query OK, 0 rows affected (0.00 sec) mysql> SELECT actor_id,@row_num AS cnt
-> FROM sakila.actor
-> WHERE (@row_num :=@row_num+1) <=1
-> ;
+----------+------+
| actor_id | cnt |
+----------+------+
| 58 | 1 |
+----------+------+
1 row in set (0.00 sec) 看一下执行计划 +----+-------------+-------+-------+---------------+---------------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------------------+---------+------+------+--------------------------+
| 1 | SIMPLE | actor | index | NULL | idx_actor_last_name | 137 | NULL | 200 | Using where; Using index |
+----+-------------+-------+-------+---------------+---------------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

想一想 如果加上ORDER BY 该怎么写?

 mysql> set @row_num :=0;
Query OK, 0 rows affected (0.00 sec) mysql> SELECT actor_id,first_name ,@row_num AS row_num
-> FROM sakila.actor
-> WHERE @row_num<=1
-> ORDER BY first_name , least(0, @row_num :=@row_num+1)
-> ; +----------+------------+---------+
| actor_id | first_name | row_num |
+----------+------------+---------+
| 2 | NICK | 2 |
| 1 | PENELOPE | 1 |
+----------+------------+---------+
2 rows in set (0.00 sec) mysql> select @row_num;
+----------+
| @row_num |
+----------+
| 2 |
+----------+
1 row in set (0.00 sec) 看一下执行计划: +----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------+
| 1 | SIMPLE | actor | ALL | NULL | NULL | NULL | NULL | 200 | Using where; Using temporary; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------+
1 row in set (0.00 sec) SELECT actor_id,first_name ,@row_num:=@row_num+1 AS row_num
FROM sakila.actor
WHERE @row_num<=1
ORDER BY first_name , least(0, @row_num :=@row_num+1)

六. UNION的巧妙改写

假设有两张用户表,一张主用户表,存放着活跃用户;一些归档用户表,存放着长期不活跃的用户。现在需要查找id 为123的客户。
先看下这个语句

 select id from users where id= 123
union all
select id from users_archived where id =123

上面的语句是可以执行的,但是效率不好,因为两张表都必须查询一次

引入自定义变量的改写:

 SELECT GREATEST(@found:=-1,id) AS id ,'users' AS which_tbl
FROM users WHERE id =123
UNION ALL
SEELCT id,'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

上面的改写非常巧妙:
第一段,如果在users查询到记录,则为@found赋值,也不会查询第二段;如果没有查询到记录,@found 为 null ,执行第二段。
第三段没有输出 ,只是简单的重置@found 为null。另外 GREATEST(@found:=-1,id) 也不会影响输出!

MySQL巧用自定义函数进行查询优化的更多相关文章

  1. MySQL学习——操作自定义函数

    MySQL学习——操作自定义函数 摘要:本文主要学习了使用DDL语句操作自定义函数的方法. 了解自定义函数 是什么 自定义函数是一种与存储过程十分相似的过程式数据库对象.它与存储过程一样,都是由SQL ...

  2. MySQL存储过程和自定义函数、Navicat for mysql、创建存储过程和函数、调用存储过程和函数的区别

    1 MySQL存储过程和函数 过程和函数,它们被编译后保存在数据库中,称为持久性存储模块(Persistent Stored Module,PSM),可以反复调用,运行速度快. 1.1 存储过程 存储 ...

  3. mysql中的自定义函数

    创建不带参数的自定义函数: 使用: 创建带参数的自定义函数: 使用: 创建具有复合结构的函数体的自定义函数:

  4. mysql 自定义函数

    原文:http://www.cnblogs.com/zhangminghui/p/4113160.html 引言 MySQL本身提供了内置函数,这些函数的存在给我们日常的开发和数据操作带来了很大的便利 ...

  5. MySQL之自定义函数

    引言 MySQL本身提供了内置函数,这些函数的存在给我们日常的开发和数据操作带来了很大的便利,比如我前面提到过的聚合函数SUM().AVG()以及日期时间函数等等,可是我们总会出现其他的需求:我们需要 ...

  6. MySQL 创建自定义函数

    语法:Create function function_name(参数列表)returns返回值类型 函数体 函数名,应合法的标识符,不应与系统关键字冲突. 一个函数应该属于某个数据库,可以使用db_ ...

  7. MySQL自定义函数(四十六)

    MySQL自定义函数 一.什么是MYSQL自定义函数? mysql当中的自定义函数,我们简称为UDF,它实际上是一种对MySQL扩展的途径,其用法与内置函数相同. 二.自定义函数应该具备哪些条件? 我 ...

  8. MySql自定义函数-关于保留小数位的特殊需求

    背景 昨天,关于价格详情接口又来了一个小需求,而且有点特别.价格显示:改为保留两位小数,没错,就是保留两位小数.大家是不是想说这没啥特别的...数据库都有函数搞定了.例如四舍五入的ROUND(x,d) ...

  9. MySQL从删库到跑路_高级(二)——自定义函数

    作者:天山老妖S 链接:http://blog.51cto.com/9291927 一.自定义函数简介 自定义函数(user-defined function UDF)是一种对MySQL扩展的途径,其 ...

随机推荐

  1. 什么时候该选用Xamarin?

    现在开发app也好,桌面程序也好,有很多不同的技术可以选择.那么,什么时候应该选用Xamarin呢? 5 questions to consider before choosing Xamarin 1 ...

  2. ap.net core 教程(三) - 新建项目

    ASP.NET Core - 新建项目 在这一章,我们将讨论如何在Visual Studio中创建一个新项目. 只要你安装了Visual Studio 2015的.net core工具,您就可以开始构 ...

  3. 【Selenium】Selenium IDE(alt+ctrl+s)

    ttp://www.yiibai.com/selenium/selenium_ide_tool_features.html     学习 Selenium IDE安装 http://seleniumh ...

  4. springMvc <form action="">提交跳转路径问题

    表单提交后,action的URL写的是, login/login.do,每次跳转后都变成login/ login/login.do,很显然是相对路径没有搞清楚. 应该弄清楚相对路径,并在JSP代码中加 ...

  5. js对手机软键盘的监听

    js还没有办法对手机软键盘直接进行监听的,但是可以有其他角度来判断软键盘是否弹起.比如输入框是否获取焦点等.focusin和focusout支持冒泡,对应focus和blur, 使用focusin和f ...

  6. bettercap实现内网Dns欺骗

    目的 让内网的所有计算机浏览网页的时候, 出现我的钓鱼页面 准备 kali系统 Bettercap dns文件 通过ifconfig查看当前计算机的ip, 我这边为, 192.168.1.150 创建 ...

  7. Asp.net MVC 如何对所有用户输入的字符串字段做Trim处理

    经常需要对用户输入的数据在插入数据库或者判断之前做Trim处理,针对每个ViewModel的字段各自做处理是我们一般的想法.最近调查发现其实也可以一次性实现的. MVC4.6中实现方式 1,实现IMo ...

  8. 开发中关于IPv6的问题

    问题: 报错信息如下 [root@kube-master iemp]# kubectl logs equipment-139404108-7mc14 -n iemp2017-05-27 05:41:0 ...

  9. .NET C#转Java没那么难,开发环境篇

    .NET C#转Java没那么难,都是面向对向的语言,而且语法还是相似的,先对比一下开发环境,再到Servlet,再到MVC,都是一样一样的,只是JAVA的配制项比较多而已,只要配好一个,后面都是co ...

  10. Oracle 分页方法研究

    1.Oracle 中的三大分页方法 1.1.通过分析函数分页 1.2.通过 ROWNUM 分页 1.3.通过 ROWID 分页 2.Oracle 分页解决方案浅析 2.1.纯后端代码完成分页 2.2. ...