BigQuery и НЕ В - PullRequest
       10

BigQuery и НЕ В

0 голосов
/ 17 мая 2018

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

Я хочу что-то вроде сводной таблицы (надеюсь, это правильное слово)

Например, у меня есть таблица с двумя столбцами: идентификатор пользователя и домен

UserID      Domain
1        |   A
1        |   B
1        |   C
2        |   A
2        |   B
3        |   A
2        |   C

Что яхочу.Мне нужна таблица, подобная следующей, которая извлекает различия по строкам

    A    B     C  
A   0    1     1 
B   0    0     0   
C   0    0     0

Как читать выходные данные?

Например, первая строка (0,1,1) Представьте всех пользователейкоторый посетил домен A (в нашем случае пользователь 1, пользователь 2 и пользователь 3) .... Все посетители домена A были в домене A (я думаю, это понятно).Также посещаемый домен B?Нет, один пользователь (в нашем случае пользователь 3) не был в домене B. Итак, у нас есть 1. Теперь мы проверяем, все ли посетители домена A были в домене C!И здесь у нас также есть пользователь, который не был в домене C. Пользователь 1 и 2 были в домене C, но пользователь 3 был не в домене C, а в домене A. Поэтому мы должны снова написать 1 ....

Вторая строка - проверьте, какие пользователи в домене B. Пользователь 1 и пользователь 2 были в домене B. Где они также в домене A?Да ... Оба ... Итак, мы должны записать 0. Пользователь 1 и пользователь 2 были в домене B?Да ... итак 0. а на домене С?Да ... Оба ... Итак, мы должны снова написать ноль.

Третья строка - для проверки домена C В домене C у нас есть посетители 1 и 2. Оба также посетили домен A, поэтому мы имеем ноль ... Оба посетили домен B?Да, также ноль и последняя запись ясна, так как они пришли из домена C .....

Короче говоря: Я хочу извлечь всех эксклюзивных посетителей каждого доменапо сравнению с другими доменами ...

Я борюсь с 2 днями с левыми соединениями и случаем, когда и так далее ... Ничего не получается.

Есть ли у кого-нибудь свои предложения?Было бы очень полезно.И да, у меня более 3 доменов.У меня около 200!

Ответы [ 3 ]

0 голосов
/ 17 мая 2018

Здесь действительно два вопроса

  1. Я хочу извлечь всех эксклюзивных посетителей каждого домена по сравнению с другими доменами ...
  2. Я хочу что-то вроде сводной таблицы

Позвольте мне ответить на ваши вопросы один за другим

Итак,

Как извлечь всех эксклюзивных посетителей каждого домена по сравнению с другими доменами ...

Ниже приведен стандарт SQL для BigQuery, в котором выровнена версия вашей матрицы

#standardSQL
WITH `project.dataset.your_table` AS (
  SELECT 1 userid, 'A' domain UNION ALL
  SELECT 1, 'B' UNION ALL
  SELECT 1, 'C' UNION ALL
  SELECT 2, 'A' UNION ALL
  SELECT 2, 'B' UNION ALL
  SELECT 3, 'A' UNION ALL
  SELECT 2, 'C' 
), temp AS (
  SELECT DISTINCT userid, domain
  FROM `project.dataset.your_table`
)
SELECT 
  a.domain domain_a, 
  b.domain domain_b, 
  COUNT(DISTINCT a.userid) - COUNTIF(a.userid = b.userid) count_of_not_in
FROM temp a
CROSS JOIN temp b
GROUP BY a.domain, b.domain
-- HAVING count_of_not_in > 0

Это приведет к

Row domain_a    domain_b    count_of_not_in  
1   A           A           0    
2   A           B           1    
3   A           C           1    
4   B           A           0    
5   B           B           0    
6   B           C           0    
7   C           A           0    
8   C           B           0    
9   C           C           0     

Я думаю, что в реальной жизни у вас не будет много нулей в этих данных, поэтому, если вы хотите сжать эту сплющенную версию - просто раскомментируйте строку с HAVING ..., чтобы вы получили "компактную" версию

Row domain_a    domain_b    count_of_not_in  
1   A           B           1    
2   A           C           1    

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

#standardSQL
WITH `project.dataset.your_table` AS (
  SELECT 1 userid, 'A' domain UNION ALL
  SELECT 1, 'B' UNION ALL
  SELECT 1, 'C' UNION ALL
  SELECT 2, 'A' UNION ALL
  SELECT 2, 'B' UNION ALL
  SELECT 3, 'A' UNION ALL
  SELECT 2, 'C' 
), domains AS ( 
  SELECT domain, ARRAY_AGG(DISTINCT userid) users
  FROM `project.dataset.your_table`
  GROUP BY domain
)
SELECT 
  a.domain domain_a, b.domain domain_b, 
  ARRAY_LENGTH(a.users) -
  (SELECT COUNT(1) 
    FROM UNNEST(a.users) user_a 
    JOIN UNNEST(b.users) user_b 
    ON user_a = user_b
  ) count_of_not_in
FROM domains a
CROSS JOIN domains b 
-- ORDER BY a.domain, b.domain   

Сейчас

Как повернуть результат выше, чтобы получить реальную матрицу?

В идеале поворот должен выполняться вне BigQuery в любом инструменте визуализации, который вы обычно используете. Но если по какой-либо причине вы хотите, чтобы это было сделано в BigQuery - это выполнимо, и здесь, в SO, огромное количество вопросов, связанных с этим. Один из самых последних, за которые я отправил ответ, - https://stackoverflow.com/a/50300387/5221944.
Он показывает, как генерировать / производить сводный запрос для достижения желаемой матрицы
Это относительно просто и может быть выполнено вручную как двухэтапный процесс (шаг 1 - сгенерировать сводный запрос и шаг 2 - запустить сгенерированный запрос) или может быть реализован с использованием любого client на ваш выбор

0 голосов
/ 18 мая 2018

очень очень большой запрос :), но он работает

DROP PROCEDURE IF EXISTS dowhile;
CREATE PROCEDURE dowhile()
BEGIN
    SELECT @domain_arr := CONCAT(GROUP_CONCAT(domain SEPARATOR ','),',') AS domain_arr FROM ( SELECT t1.domain FROM user_domain t1 WHERE 1 GROUP BY t1.domain ) AS tt;
    DROP table IF EXISTS temp_table;
    create temporary table temp_table (
        domain VARCHAR(100) not NULL
    );
    SET @domain_arr_table= @domain_arr;
    WHILE LOCATE(',', @domain_arr_table) > 0 DO
        SET @domain = SUBSTRING(@domain_arr_table,1,LOCATE(',',@domain_arr_table) - 1);
        SET @domain_arr_table= SUBSTRING(@domain_arr_table, LOCATE(',',@domain_arr_table) + 1);
        SET @s= CONCAT('ALTER TABLE temp_table ADD COLUMN ',@domain,' TINYINT DEFAULT 0');
        PREPARE stmt3 FROM @s;
        EXECUTE stmt3;
    END WHILE;
    WHILE LOCATE(',', @domain_arr) > 0 DO
        SET @domain = SUBSTRING(@domain_arr,1,LOCATE(',',@domain_arr) - 1);
        SET @domain_arr= SUBSTRING(@domain_arr, LOCATE(',',@domain_arr) + 1);
        SELECT @user_count := COUNT(*) FROM user_domain WHERE domain=@domain;
        INSERT INTO temp_table (domain) VALUES (@domain);

        SELECT @domains_should_be_1 := CONCAT(GROUP_CONCAT(domain SEPARATOR ','),',') FROM (SELECT domain FROM user_domain WHERE user_id IN (SELECT user_id FROM user_domain WHERE domain=@domain) GROUP BY domain HAVING COUNT(*) < @user_count) AS tt2;
        WHILE LOCATE(',', @domains_should_be_1) > 0 DO
            SET @domain_sb_1 = SUBSTRING(@domains_should_be_1,1,LOCATE(',',@domains_should_be_1) - 1);
            SET @domains_should_be_1= SUBSTRING(@domains_should_be_1, LOCATE(',',@domains_should_be_1) + 1);
            SET @s= CONCAT("UPDATE temp_table SET ",@domain_sb_1,"='1' WHERE domain='",@domain,"'");
            SELECT @s;
            PREPARE stmt3 FROM @s;
            EXECUTE stmt3;
        END WHILE;
    END WHILE;
END;

call dowhile();
SELECT * FROM temp_table;
0 голосов
/ 17 мая 2018

Вы не можете (легко) выразить это как матрицу. Но вы можете выразить это как таблицу с тремя столбцами:,, count.

with t as (  -- may not be necessary if the rows are already unique
      select distinct userid, domain
      from tab
     )
select t1.domain as domain1, t2.domain as domain2, count(*)
from t t1 join
     t t2
     on t1.userid = t2.userid
group by t1.domain, t2.domain;

Вы не можете легко перенести результаты в BigQuery в столбцы, если вы не знаете явно домены, которые вас интересуют. Вы можете объединить их в столбцы, если хотите.

Для заданного набора доменов в качестве столбцов можно использовать условное агрегирование:

with t as (  -- may not be necessary if the rows are already unique
      select distinct userid, domain
      from tab
     )
select t1.domain as domain1,
       sum(case when t2.domain = 'amazon.com' then 1 else 0 end) as amazon,
       sum(case when t2.domain = 'ebay.com' then 1 else 0 end) as ebay,
       sum(case when t2.domain = 'yahoo.com' then 1 else 0 end) as yahoo
from t t1 join
     t t2
     on t1.userid = t2.userid
group by t1.domain, t2.domain;
...