2011-04-21 48 views
2

我有一個MySQL數據庫,其中一列包含狀態碼。該列的類型爲int,值將只有100,200,300,400。它看起來像下面;爲清楚起見,刪除了其他列從以前的N行MySQL數據庫中獲取運行頻率分佈

id | status 
---------------- 
1  300 
2  100 
3  100 
4  200 
5  300 
6  300 
7  100 
8  400 
9  200 
10  300 
11  100 
12  400 
13  400 
14  400 
15  300 
16  300 

id字段是自動生成的,並且始終是連續的。我想要第三列顯示前10行狀態代碼頻率分佈的逗號分隔字符串。它應該看起來像這樣。

id | status | freq 
----------------------------------- 
1  300 
2  100 
3  100 
4  200 
5  200 
6  300 
7  100 
8  400 
9  300 
10  300 
11  100  300,100,200,400 -- from rows 1-10 
12  400  100,300,200,400 -- from rows 2-11 
13  400  100,300,200,400 -- from rows 3-12 
14  400  300,400,100,200 -- from rows 4-13 
15  300  400,300,100,200 -- from rows 5-14 
16  300  300,400,100  -- from rows 6-15 

我想要列出最頻繁的代碼。在兩個狀態碼具有相同頻率的情況下,首先列出的並不重要,但是我在示例中的較大範圍之前列出了較小的代碼。最後,在前10行中代碼完全不出現的地方,它也不應該列在頻率列中。

並且要非常清楚頻率字符串出現的行號確實是不是考慮到該行的狀態碼;它只是以前的行。

那麼我做了什麼?我用SQL很綠。我是一名程序員,我發現這種SQL語言有點古怪。我管理了以下自加入選擇語句。

select *, avg(b.status) freq 
from sample a 
join sample b 
on (b.id < a.id) and (b.id > a.id - 11) 
where a.id > 10 
group by a.id; 

使用集合函數avg,我至少可以證明這個概念。派生表b爲avg函數提供了正確的行,但我無法弄清楚從b計算和分組行以獲取頻率分佈,然後將頻率行摺疊爲單個字符串值的多步過程。

另外我已經嘗試使用標準存儲的函數和過程來代替內置的聚合函數,但它似乎是b派生表超出了範圍或東西。我似乎無法訪問它。從我的理解來說,編寫一個自定義的聚合函數對我來說是不可能的,因爲它似乎需要用C開發,這是我沒有受過培訓的。

以下是加載示例的sql。

create table sample (
    id int NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY(id), 
    status int 
); 

insert into sample(status) values(300),(100),(100),(200),(200),(300) 
    ,(100),(400),(300),(300),(100),(400),(400),(400),(300),(300),(300) 
    ,(100),(400),(100),(100),(200),(500),(300),(100),(400),(200),(100) 
    ,(500),(300); 

該示例有30行數據要使用。我知道這是一個很長的問題,但我只想盡可能詳細。我已經爲此工作了幾天,並且很想完成它。

感謝您的幫助。

+1

只是一個側面說明:'b.id> a.id - 如果你從來沒有刪除行並沒有插入過失敗11'纔會工作。否則,你不能依靠ID值 – 2011-04-21 08:08:11

+0

沒有差距的事實。這是正確的。但是,在這個特定的應用程序中,我可以保證id字段始終是連續的。謝謝。 – Nick 2011-04-21 21:29:13

回答

0

我知道做你要求的唯一方法是使用BEFORE INSERT觸發器。它必須是BEFORE INSERT,因爲您要更新插入行中的值,這隻能在BEFORE觸發器中完成。不幸的是,這也意味着它還沒有被分配一個ID,所以希望可以安全地假設在插入新記錄時,表中最後10條記錄是您感興趣的記錄。您的觸發器將需要獲取最後10個ID的值,並使用GROUP_CONCAT函數將它們合併成一個字符串,按COUNT排序。我一直在使用SQL Server,並且目前我無法訪問MySQL服務器來測試此操作,但希望我的語法足夠接近,至少可以讓您朝着正確的方向移動:

create trigger sample_trigger BEFORE INSERT ON sample 
FOR EACH ROW 
BEGIN 
    DECLARE _freq varchar(50); 

    SELECT GROUP_CONCAT(tbl.status ORDER BY tbl.Occurrences) INTO _freq 
    FROM (SELECT status, COUNT(*) AS Occurrences, 1 AS grp FROM sample ORDER BY id DESC LIMIT 10) AS tbl 
    GROUP BY tbl.grp 

    SET new.freq = _freq; 
END 
+0

當我第一次看到這個時,我很興奮。我想,「能這麼簡單嗎?我錯過了使用觸發器嗎?」。然後,現實打擊我。在學習觸發器之後,我不能在我的生活中弄清楚在這種情況下如何使用觸發器。據我所知,觸發器可以處理數據庫中的實際表,而不是來自查詢的結果集。如果我插入到樣本表中,它會起作用,但那不是我正在做的。所以,除非我錯過了一些東西,當然可能,我認爲這不能幫助我。不過謝謝你的評論。 – Nick 2011-04-21 21:34:07

+0

當你說「我想要第三列顯示前10行的狀態代碼的頻率分佈的逗號分隔字符串」時,我就假定了。這意味着你在你的桌子上添加了一列。所以你想在查詢中做到這一點? – 2011-04-22 13:00:58

+0

對不起,如果我不清楚這一說法。我確實提供了一個select查詢,作爲我如何試圖獲得結果集以顯示第三列的示例。如果您可以使用結果集來使觸發器工作,那將很棒。我仍在努力,並且非常感謝任何幫助。謝謝你和我一起看Joel的問題。 – Nick 2011-04-23 01:02:06

0
SELECT id, GROUP_CONCAT(status ORDER BY freq desc) FROM 
    (SELECT a.id as id, b.status, COUNT(*) as freq 
    FROM 
     sample a 
    JOIN 
     sample b ON (b.id < a.id) AND (b.id > a.id - 11) 
    WHERE 
     a.id > 10 
    GROUP BY a.id, b.status) AS sub 
GROUP BY id; 

SQL Fiddle

相關問題