簡體   English   中英

在PostgreSQL中加入多列

[英]JOIN with multiple columns in postgresql

我在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中構建交叉表或其他方法)。

您可以使用UNNESTUNNEST ”數據:

   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 
;

感謝@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

我不關心列中的輸出-我可以很容易地調整它的形狀,但是在表中存在零是很重要的。 這可能嗎?

為了獲得上述表格,需要對查詢進行一些重新設計。

首先,您必須創建一個輔助表,其中包含列名稱和活動的笛卡爾乘積

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 
;

將活動數添加到此:

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擴展名

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 |
+-----------+--------+--------+--------+--------+

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM