Группировка запросов в Redshift занимает огромное количество времени - PullRequest
1 голос
/ 01 июля 2019

У меня есть следующее требование: у меня есть таблица в следующем формате.

enter image description here

, и я хочу, чтобы она была преобразована в:

enter image description here

По сути, я хочу количество пользователей с различными комбинациями действий

Я хочу иметь этот формат, так как я хочу создатьTreeMap визуализация из этого.

Это то, что я делал до сих пор.Сначала выясните количество пользователей с группировками действий

WITH lookup AS
(
  SELECT listagg(name,',') AS groupings,
         processed_date,
         guid
  FROM warehouse.test
  GROUP BY processed_date,
           guid
)
SELECT groupings AS activity_groupings,
       LENGTH(groupings) -LENGTH(REPLACE(groupings,',','')) + 1 AS count,
       processed_date,
       COUNT(           guid) AS users
FROM lookup
GROUP BY processed_date,
         groupings

Я помещаю результаты в отдельную таблицу

Затем я делю Split и объединяюсь так:

SELECT NULLIF(SPLIT_PART(groupings,',', 1),'') AS grouping_1,
          COALESCE(NULLIF(SPLIT_PART(groupings,',', 2),''), grouping_1) AS grouping_2,
          COALESCE(NULLIF(SPLIT_PART(groupings,',', 3),''), grouping_2, grouping_1) AS grouping_3,
          num_users
   FROM warehouse.groupings) AS expr_qry
GROUP BY grouping_1,
         grouping_2,
         grouping_3

Проблема в том, что первый запрос выполняется более 90 минут, так как у меня более 250 миллионов строк.

Должен быть лучший и эффективный способ сделать это.Буду признателен за любую голову.

Спасибо

1 Ответ

2 голосов
/ 01 июля 2019

Вам не нужно использовать сложные функции работы со строками (LISTAGG(), SPLIT_PART()). Вы можете достичь того, чего хотите, с помощью функции ROW_NUMBER() и простых агрегатов.

-- Create sample data
CREATE TEMP TABLE test_data (id, guid, name) 
AS        SELECT 1::INT, 1::INT, 'cooking'
UNION ALL SELECT 2::INT, 1::INT, 'cleaning'
UNION ALL SELECT 3::INT, 2::INT, 'washing'
UNION ALL SELECT 4::INT, 4::INT, 'cooking'
UNION ALL SELECT 6::INT, 5::INT, 'cooking'
UNION ALL SELECT 7::INT, 3::INT, 'cooking'
UNION ALL SELECT 8::INT, 3::INT, 'cleaning'
;
-- Assign a row number to each name per guid
WITH name_order AS (
    SELECT guid
         , name
         , ROW_NUMBER() OVER(PARTITION BY guid ORDER BY id) row_n
    FROM test_data
) -- Use MAX() to collapse each guid's data to 1 row
, groupings AS (
    SELECT guid
         , MAX(CASE WHEN row_n = 1 THEN name END) grouping_1
         , MAX(CASE WHEN row_n = 2 THEN name END) grouping_2
    FROM name_order
    GROUP BY guid
) -- Count the guids per each grouping
SELECT grouping_1
     , COALESCE(grouping_2, grouping_1) AS grouping_2
     , COUNT(guid) num_users
   FROM groupings
GROUP BY 1,2
;
-- Output
 grouping_1 | grouping_2 | num_users
------------+------------+-----------
 washing    | washing    |         1
 cooking    | cleaning   |         2
 cooking    | cooking    |         2
...