为什么局部变量不保存值?
Why local variable doesn't save value?
我正在 mysql5.7 中编写查询以模拟 dense_rank()。我对变量范围有疑问。
我试过以不同的方式使用变量,但总是计算错误。看来变量 @total
我设置错了。
SET @row_number = 1;
SET @total =null;
SELECT
CONCAT(`u`.`name`, ' ' ,`u`.`surname`) as `User`,
SUM(`oi`.quantity * `oi`.price) as `Total amount`,
CASE
WHEN @total = SUM(`oi`.quantity * `oi`.price) THEN
@row_number
ELSE
@row_number:= @row_number + 1
END
as `Place in rank`,
@total := SUM(`oi`.quantity * `oi`.price)
FROM `user` u
LEFT JOIN `order` o ON `u`.`user_id`=`o`.user_id
LEFT JOIN `order_item` oi ON `oi`.`order_id`=`o`.order_id
WHERE `o`.`date` > DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
GROUP BY user
ORDER BY `Total amount` DESC
这是我的 fiddle https://www.db-fiddle.com/f/5yiyp6Zyt2eB5h26RT5Lmf/10
列 place in rank
的实际值为 4,3,2 但我
预计 1,1,2
在这种情况下,SELECT 在 ORDER BY 之前计算,因为没有索引可以用于 ORDER BY 子句。并且可以按任何顺序评估列(尤其是在涉及聚合函数时)。 SQL 不是程序语言。不过您可以尝试 "force" execution/evaluation 命令。在这种情况下,您(至少)需要将查询包装到一个有序的子查询中。此外 - @row_number
应初始化为 0
:
SET @row_number = 0;
SET @total = null;
SELECT *,
CASE WHEN @total = `Total amount`
THEN @row_number
ELSE @row_number:= @row_number + 1
END AS `Place in rank`,
@total := `Total amount`
FROM (
SELECT
CONCAT(`u`.`name`, ' ' ,`u`.`surname`) as `User`,
SUM(`oi`.quantity * `oi`.price) as `Total amount`
FROM `user` u
LEFT JOIN `order` o ON `u`.`user_id`=`o`.user_id
LEFT JOIN `order_item` oi ON `oi`.`order_id`=`o`.order_id
WHERE `o`.`date` > DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
GROUP BY user
ORDER BY `Total amount` DESC
) x
这现在可能有效。但你永远不知道,什么时候没有。考虑用过程语言解决此类任务,或者升级到具有window个功能的版本。
但是 - 如果被迫使用 "SQL",我会以不同的方式编写查询:
SELECT x.*,
CASE WHEN @total = `Total amount`
THEN @row_number
ELSE @row_number:= @row_number + 1 + 0*(@total := `Total amount`)
END AS `Place in rank`
FROM (
SELECT
CONCAT(`u`.`name`, ' ' ,`u`.`surname`) as `User`,
SUM(`oi`.quantity * `oi`.price) as `Total amount`
FROM `user` u
LEFT JOIN `order` o ON `u`.`user_id`=`o`.user_id
LEFT JOIN `order_item` oi ON `oi`.`order_id`=`o`.order_id
WHERE `o`.`date` > DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
GROUP BY user
ORDER BY `Total amount` DESC
) x
CROSS JOIN (SELECT @row_number := 0, @total := null) init_vars
您需要控制评估的订单行。这可以通过使用子查询首先生成总计来完成。然后使用子查询计算排名。
set @last_total = null;
set @rank = 0;
select *,
CASE
WHEN `total amount` = @last_total THEN
@rank
ELSE
@rank := @rank + 1
END
as `Place in rank`,
@last_total := `total amount`
from (
SELECT
CONCAT(`u`.`name`, ' ' ,`u`.`surname`) as `User`,
SUM(`oi`.quantity * `oi`.price) as `Total amount`
FROM `user` u
LEFT JOIN `order` o ON `u`.`user_id`=`o`.user_id
LEFT JOIN `order_item` oi ON `oi`.`order_id`=`o`.order_id
WHERE `o`.`date` > DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
GROUP BY `user`
) user_totals
order by `total amount` desc;
如果您可以使用 MySQL 8,它的特点是 dense_rank
可以为您完成这一切。
with user_totals as (
SELECT
CONCAT(`u`.`name`, ' ' ,`u`.`surname`) as `User`,
SUM(`oi`.quantity * `oi`.price) as `Total amount`
FROM `user` u
LEFT JOIN `order` o ON `u`.`user_id`=`o`.user_id
LEFT JOIN `order_item` oi ON `oi`.`order_id`=`o`.order_id
WHERE `o`.`date` > DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
GROUP BY `User`
)
select *,
dense_rank() over( order by `Total amount` desc )
from user_totals;
在这里,我将查询拆分为 Common Table Expression 来计算总数,这使得 dense_rank()
不必重复该计算。
我正在 mysql5.7 中编写查询以模拟 dense_rank()。我对变量范围有疑问。
我试过以不同的方式使用变量,但总是计算错误。看来变量 @total
我设置错了。
SET @row_number = 1;
SET @total =null;
SELECT
CONCAT(`u`.`name`, ' ' ,`u`.`surname`) as `User`,
SUM(`oi`.quantity * `oi`.price) as `Total amount`,
CASE
WHEN @total = SUM(`oi`.quantity * `oi`.price) THEN
@row_number
ELSE
@row_number:= @row_number + 1
END
as `Place in rank`,
@total := SUM(`oi`.quantity * `oi`.price)
FROM `user` u
LEFT JOIN `order` o ON `u`.`user_id`=`o`.user_id
LEFT JOIN `order_item` oi ON `oi`.`order_id`=`o`.order_id
WHERE `o`.`date` > DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
GROUP BY user
ORDER BY `Total amount` DESC
这是我的 fiddle https://www.db-fiddle.com/f/5yiyp6Zyt2eB5h26RT5Lmf/10
列 place in rank
的实际值为 4,3,2 但我
预计 1,1,2
在这种情况下,SELECT 在 ORDER BY 之前计算,因为没有索引可以用于 ORDER BY 子句。并且可以按任何顺序评估列(尤其是在涉及聚合函数时)。 SQL 不是程序语言。不过您可以尝试 "force" execution/evaluation 命令。在这种情况下,您(至少)需要将查询包装到一个有序的子查询中。此外 - @row_number
应初始化为 0
:
SET @row_number = 0;
SET @total = null;
SELECT *,
CASE WHEN @total = `Total amount`
THEN @row_number
ELSE @row_number:= @row_number + 1
END AS `Place in rank`,
@total := `Total amount`
FROM (
SELECT
CONCAT(`u`.`name`, ' ' ,`u`.`surname`) as `User`,
SUM(`oi`.quantity * `oi`.price) as `Total amount`
FROM `user` u
LEFT JOIN `order` o ON `u`.`user_id`=`o`.user_id
LEFT JOIN `order_item` oi ON `oi`.`order_id`=`o`.order_id
WHERE `o`.`date` > DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
GROUP BY user
ORDER BY `Total amount` DESC
) x
这现在可能有效。但你永远不知道,什么时候没有。考虑用过程语言解决此类任务,或者升级到具有window个功能的版本。
但是 - 如果被迫使用 "SQL",我会以不同的方式编写查询:
SELECT x.*,
CASE WHEN @total = `Total amount`
THEN @row_number
ELSE @row_number:= @row_number + 1 + 0*(@total := `Total amount`)
END AS `Place in rank`
FROM (
SELECT
CONCAT(`u`.`name`, ' ' ,`u`.`surname`) as `User`,
SUM(`oi`.quantity * `oi`.price) as `Total amount`
FROM `user` u
LEFT JOIN `order` o ON `u`.`user_id`=`o`.user_id
LEFT JOIN `order_item` oi ON `oi`.`order_id`=`o`.order_id
WHERE `o`.`date` > DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
GROUP BY user
ORDER BY `Total amount` DESC
) x
CROSS JOIN (SELECT @row_number := 0, @total := null) init_vars
您需要控制评估的订单行。这可以通过使用子查询首先生成总计来完成。然后使用子查询计算排名。
set @last_total = null;
set @rank = 0;
select *,
CASE
WHEN `total amount` = @last_total THEN
@rank
ELSE
@rank := @rank + 1
END
as `Place in rank`,
@last_total := `total amount`
from (
SELECT
CONCAT(`u`.`name`, ' ' ,`u`.`surname`) as `User`,
SUM(`oi`.quantity * `oi`.price) as `Total amount`
FROM `user` u
LEFT JOIN `order` o ON `u`.`user_id`=`o`.user_id
LEFT JOIN `order_item` oi ON `oi`.`order_id`=`o`.order_id
WHERE `o`.`date` > DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
GROUP BY `user`
) user_totals
order by `total amount` desc;
如果您可以使用 MySQL 8,它的特点是 dense_rank
可以为您完成这一切。
with user_totals as (
SELECT
CONCAT(`u`.`name`, ' ' ,`u`.`surname`) as `User`,
SUM(`oi`.quantity * `oi`.price) as `Total amount`
FROM `user` u
LEFT JOIN `order` o ON `u`.`user_id`=`o`.user_id
LEFT JOIN `order_item` oi ON `oi`.`order_id`=`o`.order_id
WHERE `o`.`date` > DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
GROUP BY `User`
)
select *,
dense_rank() over( order by `Total amount` desc )
from user_totals;
在这里,我将查询拆分为 Common Table Expression 来计算总数,这使得 dense_rank()
不必重复该计算。