2017-02-20 62 views
1

我已PostgreSQL中以下兩個表:具有多個列的JOIN PostgreSQL中

 TABLE: act_codes 
    =================== 
    activity act_desc 
    ____________________ 
     1  sleeping 
     2  commuting 
     3  eating 
     4  working 
    TABLE: data 
    =================== 
    act1_1  act_1_2  act1_3  act1_4 
    --------------------------------------------- 
     1   1   3   4 
     1   2   2   3 
     1   1   2   2 
     1   2   2   3 
     1   1   1   2 
     1   1   3   4 
     1   2   2   4 
     1   1   1   3 
     1   3   3   4 
     1   1   4   4 

的act_codes表基本上是活動表格中的(具有代碼和描述),並將該數據表包含活性代碼(在這種情況下)4個不同的時間(act1_1,act1_2,act1_3和act1_4)。

我想查詢這個來獲取每個活動的計數表。我已成功地爲每個單獨的列(在這種情況下act1_4)做到這一點是這樣的:

SELECT A.act_code, A.act_desc, COUNT (act1_4) 
    FROM act_codes AS A 
    LEFT JOIN data AS D 
    ON D.act1_4 = A.act_code 
    GROUP BY A.act_code, A.act_desc; 

這對於該列工作正常,但我有一個非常大的數字,以合作,通過列的,所以寧願它如果有一種方法可以在SQL查詢中執行此操作。


現在我有以下查詢(非常感謝banazs):

SELECT 
     ac.act_code, 
     ac.act_desc, 
     act_time, 
     COUNT(activity) AS act_count 
    FROM 
     (SELECT 
      UNNEST(array['act1_1','act1_2','act1_3','act1_4']) AS act_time, 
      UNNEST(array[d.act1_1, d.act1_2, d.act1_3, d.act1_4]) AS activity 
     FROM 
      data d) t 
    RIGHT JOIN 
     act_codes ac ON t.activity = ac.act_code 
    GROUP BY 
     ac.act_code, 
     ac.act_desc, 
     act_time, activity 
    ORDER BY 
     activity, 
     act_time 
    ; 

,輸出:

act_code  act_desc  act_time  act_count 
    --------------------------------------------------------- 
     1   sleeping   act1_1   10 
     1   sleeping   act1_2   6 
     1   sleeping   act1_3   2 
     2   commuting   act1_2   3 
     2   commuting   act1_3   4 
     2   commuting   act1_4   2 
     3   eating    act1_2   1 
     3   eating    act1_3   3 
     3   eating    act1_4   3 
     4   working    act1_3   1 
     4   working    act1_4   5 

這基本上就是我一直在尋找。理想情況下,可以以某種方式添加零計數的行,但是我猜測這可能是一個單獨的過程(例如,在R中構建交叉表)。

+0

請確認:'data'表中每次都有一個單獨的列嗎? – Richard

+0

重做數據庫設計。 –

+0

數據表中的列對應於時間段(例如,act1_1是前5分鐘,act1_2是第二個等)。不是我的設計 - 所以我必須使用這種形狀的數據。 –

回答

0

爲了實現上述表格,查詢需要重新設計一點。

首先,你必須創建一個包含列名和活動的cartesian product輔助表:

SELECT 
    * 
FROM 
    act_codes ac 
-- if you have lots of columns you can query their 
-- names from the information_schema.columns system 
-- table 
CROSS JOIN -- the CROSS JOIN combine each rows from both tables 
    (SELECT 
     column_name 
    FROM 
     information_schema.columns 
    WHERE 
     table_schema = 'stackoverflow' 
     AND table_name = 'data' 
     AND column_name LIKE 'act%') cn 
; 

添加activites的數量這樣的:

SELECT 
    ac.act_code, 
    ac.act_desc, 
    cn.column_name, 
    -- the COALESCE add zero values where the original is NULL 
    COALESCE(ad.act_no ,0) AS act_no 
FROM 
    act_codes ac 
CROSS JOIN 
    (SELECT 
     column_name 
    FROM 
     information_schema.columns 
    WHERE 
     table_schema = 'stackoverflow' 
     AND table_name = 'data' 
     AND column_name LIKE 'act%') cn 
-- you need to use LEFT JOIN to preserve all rows 
-- from the cartesian product 
LEFT JOIN 
    (SELECT 
     t.column_name, 
     t.act_code, 
     COUNT(*) AS act_no 
    FROM 
     (SELECT 
      UNNEST(array['act1_1','act1_2','act1_3','act1_4']) AS column_name, 
      UNNEST(array[d.act1_1, d.act1_2, d.act1_3, d.act1_4]) AS act_code 
     FROM 
      data d) t 
    GROUP BY 
     t.column_name, 
     t.act_code) ad ON ad.act_code = ac.act_code AND ad.column_name = cn.column_name 
; 

要格式化結果看起來像你的是可能的,但有點混亂。您需要創建兩個表,第一個必須包含前一個查詢的結果集,第二個包含列名。

CREATE TABLE acts AS 
    SELECT 
     ac.act_code, 
     ac.act_desc, 
     cn.column_name, 
     COALESCE(ad.act_no ,0) AS act_no 
    FROM 
     act_codes ac 
    CROSS JOIN 
     (SELECT 
      column_name 
     FROM 
      information_schema.columns 
     WHERE 
      table_schema = 'stackoverflow' 
      AND table_name = 'data' 
      AND column_name LIKE 'act%') cn 
    LEFT JOIN 
     (SELECT 
      t.column_name, 
      t.act_code, 
      COUNT(*) AS act_no 
     FROM 
      (SELECT 
       UNNEST(array['act1_1','act1_2','act1_3','act1_4']) AS column_name, 
       UNNEST(array[d.act1_1, d.act1_2, d.act1_3, d.act1_4]) AS act_code 
      FROM 
       data d) t 
     GROUP BY 
      t.column_name, 
      t.act_code) ad ON ad.act_code = ac.act_code AND ad.column_name = cn.column_name 
; 

CREATE TABLE column_names AS 
    SELECT 
     column_name 
    FROM 
     information_schema.columns 
    WHERE 
     table_schema = 'stackoverflow' 
     AND table_name = 'data' 
     AND column_name LIKE 'act%' 
; 

安裝tablefunc extension

CREATE EXTENSION tablefunc; 

它提供了crosstab()函數並使用它可以獲得描述的輸出。

SELECT 
    * 
FROM 
    crosstab(
     'SELECT act_desc, column_name, act_no FROM acts ORDER BY 1', 
     'SELECT * FROM column_names' 
    ) 
AS 
    ct (
     "act_desc" text, 
     "act1_1" int, 
     "act1_2" int, 
     "act1_3" int, 
     "act1_4" int 
     ); 
; 

+-----------+--------+--------+--------+--------+ 
| act_desc | act1_1 | act1_2 | act1_3 | act1_4 | 
+-----------+--------+--------+--------+--------+ 
| commuting |  0 |  3 |  4 |  2 | 
| eating |  0 |  1 |  3 |  3 | 
| sleeping |  10 |  6 |  2 |  0 | 
| working |  0 |  0 |  1 |  5 | 
+-----------+--------+--------+--------+--------+ 
+0

完美!謝謝。 –

1

您可以「逆透視」的使用UNNEST數據:

SELECT 
     UNNEST(array['act1_1','act1_2','act1_3','act1_4']) AS column_name, 
     UNNEST(array[d.act1_1, d.act1_2, d.act1_3, d.act1_4]) AS value 
    FROM 
     data d 
    ; 

計數活動:

SELECT 
    ac.act_code, 
    ac.act_desc, 
    COUNT(*) 
FROM 
    (SELECT 
     UNNEST(array['act1_1','act1_2','act1_3','act1_4']) AS column_name, 
     UNNEST(array[d.act1_1, d.act1_2, d.act1_3, d.act1_4]) AS val 
    FROM 
     data d) t 
INNER JOIN 
    act_codes ac ON t.val = ac.act_code 
GROUP BY 
    ac.act_code, 
    ac.act_desc 
; 
+0

我剛剛嘗試過,它可以用作重塑步驟,但我不確定如何將其轉換爲我希望生成的計數表。 –

+0

我更新了我的答案,現在我給出完整的解釋。 – banazs

0

感謝@banazs - 這是在幫助方面確實有用我瞭解如何構建查詢喜歡這個。

但是,我仍然難以安排查詢來拆分輸出,以便每次都有一列計數。道歉 - 我認爲這裏的標籤有點混亂(act1_1指的是在time_1完成的活動,'act1_2'指的是time_2等。)。我想結果去看起來像這樣:

act_code act_desc  count_act1_1 count_act1_2 count_act1_3 count_act1_4 
    ---------------------------------------------------------------------------------------- 
     1  sleeping   10    6    2    0 
     2  commuting   0    3    4    2 
     3  eating    0    1    3    3 
     4  working    0    0    1    5 

我不關心列輸出是 - 我可以很容易地重新塑造它,但零的存在於表中是很重要的。這可能嗎?

+0

我爲此創建了一個新的答案,它有點複雜,所以請隨時提問。 – banazs