Mysql-Cross join 用户自定义变量 根据输入值,查询总和小于等于输入值的记录
在开发中遇到的一个需求,来记录一下方案和遇到的问题
需求场景
因为公司资金问题,需要在公司的后台管理中,输入具体的打款金额,来根据用户的提现优先级进行打款,也就是说有 A>B>C>D…等级, 在页面上输入能够打款的金额,来按照优先级进行打款,打款金额不能超过具体输入的金额,然后查出匹配的数据调用第三方平台进行打款。
假设表中有4条提现数据
4条数据对应的金额和等级
amount:level
12、 A;
43、 S;
54、 B;
75、 C;
例1:输入值:60 结果值:43,12
例2:输入值:100 结果值:43,12
例3:输入值:200 结果值:43,12,53,73
SQL实现
SELECT a.id, a.level , a.amount
FROM (select ar.id, ar.level, ar.amount, (@sum := @sum + amount) as totalSum
from table1 ar
ORDER BY level ) a
cross join (select @sum := 0) params
WHERE totalSum < 100;
实现思路
- @sum 自定义一个mysql 变量。
- 然后是用 cross join 来进行一个乘积连接。
- 根据输入金额做为where条件。
cross join(笛卡尔连接)
cross join是mysql中的一种连接方式,区别于内连接和外连接,对于cross join连接来说,其 实使用的就是笛卡尔连接。在MySQL中,当CROSS JOIN不使用WHERE子句时,CROSS JOIN产生了一个结果集,该结果集是两个关联表的行的乘积。通常,如果每个表分别具有n和m行,则结果集将具有n*m行
引用https://www.w3resource.com/mysql/advance-query-in-mysql/mysql-cross-join.php的图片,如图演示了cross join的过程,这个过程其实就是笛卡尔连接查询
测试时出现的问题:
当执行sql时,偶尔会查询不出数据来(数据是没有问题的,就是查询不出来)
经排查,最终怀疑是 @sum := 0 的问题,怀疑是Mysql的优化器对执行的sql优化时,把变量给优化掉了,导致sql不按照原有的方式执行了
解决方案
后来调整了一下cross join 连接的顺序, 将自定义变量的select 调整在前面,问题就可以解决了。
SELECT a.id, a.level , a.amount
FROM (select @sum := 0) params
cross join (select ar.id, ar.level, ar.amount, (@sum := @sum + amount) as totalSum
from table1 ar
ORDER BY level ) a
WHERE totalSum < 100;
下面是关于自定义变量的属性和限制
- 使用自定义变量的查询,无法使用查询缓存。
- 不能在使用常量或者标识列的地方使用自定义变量,例如表名、列明和LIMIT子句中。
- 用户自定义变量的生命周期是在一个连接中有效,所以不能使用它们来做连接间的通信。
- 如果使用连接池或者持久化连接,自定义变量可能让看起来毫无关系的代码发生交互。
- 在5.0版本之前,是大小写敏感的,所以要注意代码在不同版本之间的兼容性问题。
- 不能显示的声明自定义变量的类型。它是一个动态类型。整数初始化为0,浮点型初始化为0.0,字符串初始化为’’。
- MySQL优化器在某些场景下可能会将这些变量优化掉,这可能导致代码不按预想的方式运行。
- 赋值的顺序和赋值的时间点并不总是固定的,这依赖于优化器的决定。
- 赋值符号:=的优先级非常低,所有要注意赋值表达式应该使用明确的括号。
- 使用未定义变量不会产生任何错误,如果没有意识到这一点,非常容易犯错
用户自定义变量,是真的香。
参考文章: