2017-02-20 15 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

Повторить дизайн db. –

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

Добавление количество Активности к этому:

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; 

Он предоставляет функцию кросс-таблицы(), и с ее помощью вы можете получить описанный вывод.

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

Вы можете данные "UNPIVOT" с помощью 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

 Смежные вопросы

  • Нет связанных вопросов^_^