2014-10-09 98 views
1

我有這個MySQL表,我想在同一個表上使用COUNT()和SUM()更新多個列(子,大小)。在同一個表中的Mysql子查詢COUNT返回NULL

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 

注:

大小= privatesize +兒童privatesize

NULL = 「」,只是爲了演示

讓我們更新現在只有一個c olumn,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) 

正常工作。

但我不想使用函數,因爲後來我需要更新多列(例如:大小),我不想調用每個列的函數。

對於我曾嘗試此查詢:

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。

如果我更改GROUP BY:idpath,即使使用相同的方法(使用相同的子查詢邏輯)在SELECT中也不起作用,將返回NULL或返回多行(對於每個組都有正確的計數)。 看起來像COUNT()在子查詢上不像平常一樣工作。

我在這個查詢中缺少什麼?有人可以解釋究竟是什麼導致了這種行爲,或者我錯了嗎?

Online SELECT Test

感謝。

+0

你的子查詢高出近40%應在'path'上分組,而不是'userid'。自行執行子查詢。您應該看到正確的結果,並且允許在「FROM」子句子查詢中使用「COUNT(*)」聚合。 – 2014-10-10 02:01:13

+0

感謝您的回覆。這不正確。按路徑子查詢分組會返回多行,因此在這種情況下不會計算所有子項。之前測試過,現在只是爲了確保。謝謝。 – h2odev 2014-10-10 02:08:32

+0

我想在線上傳測試,但sqlfiddle已關閉。 – h2odev 2014-10-10 02:11:41

回答

1

經過多次嘗試後,我陷入了一個簡單的「詭計」,我會在這裏發帖。我對我的解決方案不滿意,但最終它的工作。我使用User Defined Variables作爲從User Defined Function多個變量(INT變量)「返回」的方式。

的GetChildren(ROWID,用戶ID)功能:

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(@childrensize,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); 

性能比使用功能(在上面的方法)