同一个表中的Mysql子查询COUNT返回NULL

时间:2014-10-09 23:48:35

标签: mysql join count subquery

我有这个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()在子查询中不像往常那样工作。

此查询中缺少什么?有人能解释一下究竟是什么导致了这种行为,或者我完全错了?

Online SELECT Test

感谢。

1 个答案:

答案 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%