2016-04-18 1 views
0

次のクエリを使用してdobから人の年齢を計算し、10年間隔で年齢をグループ化して頻度グラフを作成します。周波数数は可変ですが可変のbinサイズのMYSQLクエリを書くには?

私は、10年を常に使用する必要はなく、5年間隔、20年間隔または任意の範囲で年齢をグループ化するなど、ユーザーがビンサイズを選択できるようにしたいと考えています。

ビンサイズ(現在10年間)をパラメータとして渡すか、クエリを実行する直前にビンがあらかじめ設定されている別のテーブルから取得できるように、クエリを書き直す必要はありますか?

明らかに私はハードコーディングされたCASEを同じ方法で使用することはできません。それはできますか?

SELECT 
    CASE 
     WHEN age = -1    THEN 0 -- null dob 
     WHEN age >= 0 AND age < 11 THEN 1 
     WHEN age >= 11 AND age < 21 THEN 2 
     WHEN age >= 21 AND age < 31 THEN 3 
     WHEN age >= 31 AND age < 41 THEN 4 
     WHEN age >= 41 AND age < 51 THEN 5 
     WHEN age >= 51 AND age < 61 THEN 6 
     WHEN age >= 61 AND age < 71 THEN 7 
     WHEN age >= 71 AND age < 81 THEN 8 
     WHEN age >= 81 AND age < 91 THEN 9 
     WHEN age >= 91 AND age < 101 THEN 10 
     WHEN age > 100    THEN 11 
    END AS Age_Group, 
    COUNT(age) AS Number_In_Group 
FROM 
     -- this sub query calculates the age from the dob 
     -- returning -1 if dob is null 
    (SELECT  
    IFNULL( 
      DATE_FORMAT(NOW(), '%Y') 
      - DATE_FORMAT(dob, '%Y') 
      - (DATE_FORMAT(NOW(), '00-%m-%d') < DATE_FORMAT(dob, '00-%m-%d')) 
      , - 1 
     ) AS age 
    FROM 
     people 
    ) AS table_age 

GROUP BY Age_Group 

これは私が私自身がそう、それは他の誰を助けた場合に私の答えを投稿しています、これを解決しているかもしれないと思う、次の標準的な出力

Age_Group Number_In_Group 
0   55    
2   1     
3   37    
4   47    
5   51    
6   112    
7   139    
8   70    
9   30    
10   6 

答えて

0

を生成します。 (または誰かがより良い方法を持っている場合)

トリックはストアドプロシージャを使用しているようです(ビンの増分をパラメータとして渡すことができます)。

プロシージャはまずビンの一時表を作成し、目的のbin_sizeパラメータを使用して各ビンの最小および最大の年齢を設定してwhileループに移入します。

最後に、通常のグループ化されたカウントを実行し、その中の頻度ビンを含む一時テーブルまでの生年月日(年齢を計算する)で表を結合してビン・ラベルをグループ化します。カウントがゼロであっても、すべてのビンラベルが返されるように、左結合が使用されます。

驚くほど速く走っているように見えます。例えば、1000の出生日と15年のビン頻度を使用する0.03秒です。

用法: 例えば15年の段階で周波数テーブルを取得するためには、これはコード

DELIMITER $$ 

CREATE PROCEDURE age_frequency_count(IN bin_size INT) 
BEGIN 
    DECLARE bin_min_age INT;   -- minimum age for bin 
    DECLARE bin_max_age INT;   -- maximum age for bin 
    DECLARE bin_label VARCHAR(8); -- label for bin 

-- ######################################### 
    -- make a temporary table for the bins if it doesn't exist 
    CREATE TEMPORARY TABLE IF NOT EXISTS tbl_bins 
     (minage INT, maxage INT, agegroup VARCHAR(30)) ; 

-- #########################################   
    -- empty it, in case it did already exist  
    DELETE FROM tbl_bins; 

-- #########################################  
    -- loop round, populating temp table using bin_size up to around 100 yrs old 
    SET bin_min_age = 0; 
    WHILE (bin_min_age + bin_size) < 100 DO 
     SET bin_max_age = bin_min_age + bin_size ; 
     SET bin_label = CONCAT(bin_min_age ,' to ' , bin_max_age); 
     INSERT INTO tbl_bins VALUES (bin_min_age, bin_max_age, bin_label); 
     SET bin_min_age = bin_max_age; 
    END WHILE; 

    -- now insert bin for any age above around 100 yrs old (up to 200 yrs old) 
    INSERT INTO tbl_bins VALUES (bin_min_age, 200, CONCAT('over ', bin_max_age)); 

    -- and a bin for any ages of -1 that were generated due to a null dob 
    INSERT INTO tbl_bins VALUES (-1, -1, 'unknown'); 

-- ######################################### 
-- finally select the age counts grouped into bins by joining the 'people' 
-- table containing the dob to the bins table we just made and populated 
SELECT 
    agegroup, 
    COUNT(age) AS NumberInGroup 
FROM 
    tbl_bins 
    LEFT JOIN -- LEFT so that we will still get zero counts if necessary 
     (SELECT -- next few lines calculate the age from the dob in the people table 
     IFNULL(
       DATE_FORMAT(NOW(), '%Y') 
       - DATE_FORMAT(member_dob, '%Y') 
       - (DATE_FORMAT(NOW(), '00-%m-%d') < DATE_FORMAT(member_dob, '00-%m-%d')) 
      , - 1 -- if dob is null set the age to -1 
       ) AS age 
     FROM people 
    ) AS tbl_ages 

    ON 
    (tbl_ages.age > tbl_bins.minage AND tbl_ages.age <= tbl_bins.maxage) -- normal bin 
    OR (tbl_ages.age = tbl_bins.maxage) -- to account for age of -1 

GROUP BY agegroup; 

-- ######################################### 
END$$ 

DELIMITER ; 
ある

CALL age_frequency_count(15); 

使用してこの手順を呼び出します