我有这个MySQL表,我想在同一个表上使用COUNT()和SUM()更新多个列(子项,大小)。
mytable的
id parentid name userid path children privatesize size
=======================================================================
1 0 Test-1 1 NULL 5 20 125
2 0 Test-2 1 NULL 0 15 15
3 1 Test-3 1 /1/ 3 25 75
4 1 Test-4 1 /1/ 0 30 30
5 3 Test-5 1 /1/3/ 0 10 10
6 3 Test-6 1 /1/3/ 1 30 40
7 6 Test-7 1 /1/3/6/ 0 10 10
8 0 Test-8 2 NULL 0 20 20
注意:
size = privatesize + children privatesize
NULL =“”,仅用于演示
现在让我们更新一列children
列。
现在我使用MySQL存储函数来计算孩子:
DELIMITER $$
CREATE DEFINER=`dbuser`@`localhost`
FUNCTION `getchildren`( rowid INT, uid INT ) RETURNS INT(11)
BEGIN
DECLARE children INT DEFAULT 0;
SELECT COUNT( `mytable`.`id` ) INTO children
FROM `dbname`.`mytable`
WHERE `mytable`.`path` LIKE CONCAT( '%/',rowid ,'/%' ) AND `mytable`.`userid` = uid;
RETURN children;
END
测试函数getchildren:
SELECT dbname.getchildren( 1, 1 );
返回5号
要更新行(示例ID 2和5),我使用此查询:
UPDATE `dbname`.`mytable`
SET `children` = getchildren( `mytable`.`id` , `mytable`.`userid` )
WHERE `mytable`.`id` IN ( 2, 5 )
并且工作正常。
但是我不想使用函数,因为稍后我需要更新多个列(例如:size),我不想为每个列调用函数。
为此,我尝试了这个查询:
UPDATE `dbname`.`mytable` mt
INNER JOIN (
SELECT `mytable`.`path` AS path, COUNT( `mytable`.`id`) AS countid
FROM `dbname`.`mytable`
GROUP BY `mytable`.`userid`
) sub ON `sub`.`path` LIKE CONCAT( "%/", `mt`.`id` , "/%" )
SET `mt`.`children` = `sub`.`countid`
WHERE `mt`.`id` IN ( 2, 5 );
实际上没有成功,这会将子项的值更改为NULL。
即使是相同的方法(使用相同的子查询逻辑)在SELECT中也不起作用,如果我更改GROUP BY:id
或{{},则返回NULL或返回多行(每个组的计数正确) 1}}。
看起来COUNT()在子查询中不像往常那样工作。
此查询中缺少什么?有人能解释一下究竟是什么导致了这种行为,或者我完全错了?
感谢。
答案 0 :(得分:1)
经过多次尝试后,我陷入了一个简单的"技巧"我会在这里发帖我对我的解决方案不满意,但最后它起作用了。我使用User Defined Variables作为"返回"的方式来自User Defined Function多个变量(INT变量)。
getchildren(rowid,userid)函数:
DELIMITER $$
CREATE DEFINER=`dbuser`@`localhost`
FUNCTION `getchildren`( rowid INT, uid INT ) RETURNS INT(11)
BEGIN
DECLARE children INT DEFAULT 0;
SET @childrensize := 0;
SELECT
COUNT( `mytable`.`id` ),
SUM( `mytable`.`privatesize` )
INTO children, @childrensize
FROM `dbname`.`mytable`
WHERE
`mytable`.`path` LIKE CONCAT( '%/',rowid ,'/%' )
AND `mytable`.`userid` = uid;
RETURN children;
END
测试新功能:
SELECT `dbname`.getchildren( 1, 1 ) AS children, @childrensize AS size;
返回:
Children size
===============
5 105
现在让我们更新ID为2和5的行(包括子项和大小)
UPDATE `dbname`.`mytable`
SET
`children` = getchildren( `mytable`.`id`, `mytable`.`userid` ),
`size` = `privatesize` + IFNULL( @childrensize, 0 )
WHERE `mytable`.`id` IN ( 2, 5 )
工作正常!
逻辑很简单,每次调用函数getchildren时,他都会更新用户定义的变量 @childrensize 。如果该行没有子节点,则函数将@childrensize设置为NULL,因为IFNULL(@ childrenrensize,0)是必需的。
这样,该函数将为每一行调用一次并更新多列。
<强>更新强>
以下是此案例的正确解决方案:
UPDATE `dbname`.`mytable` mt
LEFT JOIN (
SELECT `mtc1`.`id`, count(*) numchildren, sum( `mtc2`.`privatesize` ) AS tsize
FROM `tsdata`.`mytable` mtc1, `tsdata`.`mytable` mtc2
WHERE `mtc2`.`path` LIKE CONCAT( '%/',mtc1.id,'/%' )
GROUP by `mtc1`.id)
mtc ON `mtc`.`id` = `mt`.`id`
SET
`mt`.`children`= IFNULL( `mtc`.numchildren, 0 ),
`mt`.`size` = `mt`.`privatesize` + IFNULL( `mtc`.tsize, 0 )
WHERE `mt`.`id` in ( 2, 5 );
性能比使用功能(在上面的方法上)高出近40%