2011-08-09 83 views
13

我想在SQL(MySQL)中編寫一個存儲過程來計算第二和第三四分位數的平均值。在SQL中計算2,3四分位數平均值

換句話說,我已經記錄了加載URL所需的時間。記錄是(id,url,time),它們是每個URL的許多度量。我想要做的是爲每個URL刪除最低和最高的25%(即低和高四分位數),並計算其餘25%-75%的加載時間的平均值。並將其存儲到另一個表中。

我看到了一些MS SQL的例子,看起來相對容易。但我不得不使用MySQL其中:

  • LIMIT子句不支持百分比(沒有模擬,選擇排名前25%)
  • LIMIT子句不支持其論點是變量(僅適用於常量)
  • 功能不支持動態SQL(例如PREPARE和EXECUTE)

而且我得到了儘可能的位置:

create procedure G(
    IN val VARCHAR(10) 
) 
Begin 
    select @cnt:=count(*) from test where a=val; 
    select @of:= @cnt /4; 
    SELECT @len:= @cnt/2; 
    Prepare stmt from 'select * from test where a="a" LIMIT ?,?'; 
    execute stmt using @of, @len; 
END; 

我可以用PHP編寫它,但在SQL中思考它會有更好的整體性能。我會非常感謝一些幫助。

+2

Joe Celko的'Smart Smart'SQL有一章統計(模式,中位數,差異等)這是值得的購買價格。 –

回答

2

看答案和註釋通過@Richard又名cyberkiwi在this question

Select * 
from 
(
    SELECT tbl.*, @counter := @counter +1 counter 
    FROM (select @counter:=0) initvar, tbl 
    ORDER BY ordcolumn 
) X 
where counter >= (25/100 * @counter) and counter <= (75/100 * @counter); 
ORDER BY ordcolumn 
0

這個怎麼樣?

prepare stmt from select concat('select * from test where a="a" LIMIT ',@of,@len); 
execute stmt; 
+0

1.這仍然是動態的SQL,並沒有什麼不同,我寫的 2.這行產生的語法錯誤,儘管它看起來對我很好 – munch

0

看看這個用MySQL計算百分點的優秀例子。我已經在一些非常大的數據集上取得了巨大的成功。

http://planet.mysql.com/entry/?id=13588

。注意到有關group_concat_max_len的部分 - 這是非常重要的。將此值設置爲最大允許值(這是您對最大數據包大小的設置)將確保如果它構建的字符串變得太大,您將得到正確的錯誤,而不僅僅是「截斷字段」警告。

SET @@group_concat_max_len := @@max_allowed_packet; 

我會做的是使用這個函數來計算25和第75百分位數(可在一個單一的查詢來完成),然後通過對數據運行的第二個查詢計算你的剩餘數據的平均值。

<?php 
$lowVal = /* result of query getting the 25%ile value */; 
$highVal = /* result of query getting the 75%ile value */; 

$strSQL = "SELECT AVG(`field`) AS myAvg 
      FROM `table` 
      WHERE { your_existing_criteria_goes_here } 
       AND `filter_field` BETWEEN '{$lowVal}' AND '{$highVal}';" 
/* Run the query and extract your data */ 
?> 

希望所有有道理,並與你的問題:)

+1

我知道這不是一個存儲過程,並在PHP中完成,我相信有人在那裏可以重寫它作爲一個存儲過程,它能夠從單個查詢中獲得25%ile和75%ile值,然後將它們應用於主要篩選查詢,但以上是我將如何處理它。然後,它允許您在代碼的其他區域使用25%ile和75%的值,例如當爲您正在構建的數據表或圖表創建標題或圖例信息時;) –

0

幫助你爲什麼不只是使用一個查詢是這樣的:

select url, avg(time) 
from mytable A 
where time > 
     (select min(B.time) + ((max(B.time)-min(B.time))/100*25) 
      from mytable B where B.url = A.url) 
and time < 
     (select max(B.time) - ((max(B.time)-min(B.time))/100*25) 
      from mytable B where B.url = A.url) 
group by url; 
+0

這不一定會讓您獲得第2和第3四分位數。如果時間是均勻分佈的,那麼它將會接近,但因爲這是不符合要求的。 – Dason

1

您可以通過創建四分位數使用IF將它們設置爲零如果處於錯誤的四分位數:

我們假設,原始數據表由

創建
DROP TABLE IF EXISTS `rawdata`; 
CREATE TABLE `rawdata` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `url` varchar(250) NOT NULL DEFAULT '', 
    `time` int(11) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `time` (`time`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

(以及人口稠密)。

我們還假設四分位表的數據由

DROP TABLE IF EXISTS `quartiles`; 
CREATE TABLE `quartiles` (
    `url` varchar(250) NOT NULL, 
    `Q1` float DEFAULT '0', 
    `Q2` float DEFAULT '0', 
    `Q3` float DEFAULT '0', 
    `Q4` float DEFAULT '0', 
    PRIMARY KEY (`url`), 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

(和空)創建的。

然後填充從RAWDATA四分位數的程序看起來像

DELIMITER ;; 

CREATE PROCEDURE `ComputeQuartiles`() 
    READS SQL DATA 
BEGIN 
    DECLARE numrows int DEFAULT 0; 
    DECLARE qrows int DEFAULT 0; 
    DECLARE rownum int DEFAULT 0; 
    DECLARE done int DEFAULT 0; 
    DECLARE currenturl VARCHAR(250) CHARACTER SET utf8; 
    DECLARE Q1,Q2,Q3,Q4 float DEFAULT 0.0; 
    DECLARE allurls CURSOR FOR SELECT DISTINCT url FROM rawdata; 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET currenturl=''; 

    OPEN allurls; 
    FETCH allurls INTO currenturl; 
    WHILE currenturl<>'' DO 
     SELECT COUNT(*) INTO numrows FROM rawdata WHERE url=currenturl; 
     SET qrows=FLOOR(numrows/4); 
     if qrows>0 THEN 
      -- Only session parameters can be recalculated inside a query, 
      -- so @rownum:[email protected]+1 will work, but rownum:=rownum+1 will not. 
      SET @rownum=0; 
      SELECT 
       SUM(IFNULL(QA,0))/qrows, 
       SUM(IFNULL(QB,0))/qrows, 
       SUM(IFNULL(QC,0))/qrows, 
       SUM(IFNULL(QD,0))/qrows 
      FROM (
       SELECT 
        if(@rownum<qrows,time,0) AS QA, 
        if(@rownum>=qrows AND @rownum<2*qrows,time,0) AS QB, 
        -- the middle 0-3 rows are left out 
        if(@rownum>=(numrows-2*qrows) AND @rownum<(numrows-qrows),time,0) AS QC, 
        if(@rownum>=(numrows-qrows),time,0) AS QD, 
        @rownum:[email protected]+1 AS dummy 
       FROM rawdata 
       WHERE url=currenturl ORDER BY time 
      ) AS baseview 
      INTO Q1,Q2,Q3,Q4 
      ; 
      REPLACE INTO quartiles values (currenturl,Q1,Q2,Q3,Q4); 
     END IF; 

     FETCH allurls INTO currenturl; 
    END WHILE; 
    CLOSE allurls; 

END ;; 

DELIMITER ; 

的要點是:

  • 使用遊標循環中的URL(或改編的樣品接受URL作爲一個參數)
  • 對於每一個網址找到總行數
  • 做一些微不足道的數學來排除中間行,如果(rowcount % 4) != 0
  • 選擇的所有原始行的URL,而time值分配給QA-QD的一個,這取決於行號,該查詢作爲子查詢分配其他QX值0
  • 使用到另一個,這總結起來,歸一化值
  • 使用此superquery的結果更新四分表

我用18432個的原始行,url=concat('http://.../',floor(rand()*10)), time=round(rand()*10000)一個8x1.9GHz機測試這一點,它在0.50-0.54sec

一貫完成