如何在 Google Bigquery 中将字符串转换为列名?

How to convert strings into column names in Google Bigquery?

我在 Google BigQuery 中有一个大型数据集,其中包含数百万行我正在尝试清理的脏数据(应用程序跟踪)。我的问题之一是,对于应用程序中触发的不同事件,相同的数据被发送到不同的列。我的意思是,也许该国家/地区对于某些事件被发送到自定义维度 1,但对于其他事件则发送到自定义维度 147。我不能 post 实际数据,但是 SELECT * FROM table_with_dirty_data 会产生这样的结果:

date | session | eventAction | cd001 | cd002    | cd004    | cd005 
-----|---------|-------------|-------|----------|----------|-------
1    | 1       | 'event_1'   | '1'   | 'Pizza'  | null     | '21'
1    | 1       | 'event_2'   | '10'  | '25'     | 'Pizza'  | '14.56'
1    | 1       | 'event_3'   | '3.1' | null     | '15'     | 'France'
1    | 2       | 'event_1'   | '6'   | 'Burger' | null     | '21'
1    | 2       | 'event_2'   | '21'  | '25'     | 'Burger' | '12.6'

这里的最终目标是得到一个可以分析的干净 table。像这样:

date | session | eventAction | country  | vendor   | product  | price
-----|---------|-------------|----------|----------|----------|-------
1    | 1       | 'event_1'   | 'France' | '1'      | 'Pizza'  | '14.56'
1    | 1       | 'event_2'   | 'France' | '1'      | 'Pizza'  | '14.56'
1    | 1       | 'event_3'   | 'France' | '1'      | 'Pizza'  | '14.56'
1    | 2       | 'event_1'   | 'Spain'  | '25'     | 'Burger' | '12.6'
1    | 2       | 'event_2'   | 'Spain'  | '25'     | 'Burger' | '12.6'

我知道有些事件需要一定程度的统计插补和数据类型转换,但现在我只关心将每个变量放入其自己的列中。所以我创建了一个辅助 table(我们称它为 matrix),如下所示:

event_name | variable_1 | variable_2 | variable_3
-----------|------------|------------|----------
'event_1'  | 'cd020'    | 'cd035'    | 'cd120'
'event_2'  | 'cd020'    | 'cd146'    | 'cd056'
'event_3'  | 'cd001'    | 'cd020'    | 'cd035'

依此类推,其中 variable_# 列每个单元格中的值是可以找到信息的 table_with_dirty_data 中的列名称。也就是说,对于具有 event_name 'event_1' 和 'even_2' 的事件,可以在名为 cd020 的列中找到 variable_1,但在名为 cd001 的列中对于 event_name 'event_3' 的事件。所以基本上 matrix 所做的是将每个变量为每个事件发送到哪个自定义维度。

我在 table_with_dirty_data 中有数百个不同的事件,并且 matrix 包含所有 200 个 GA 自定义维度,所以做一些像

SELECT 
  CASE 
    WHEN event_name = 'event_1' THEN cd020
    WHEN event_name = 'event_2' THEN cd020
    WHEN event_name = 'event_3' THEN cd001
  END AS variable_1
  , CASE
      WHEN event_name = 'event_1' THEN cd035
      WHEN event_name = 'event_2' THEN cd146
      WHEN event_name = 'event_3' THEN cd020
    END AS variable_2
  , CASE
      WHEN event_name = 'event_1' THEN cd120
      WHEN event_name = 'event_2' THEN cd056
      WHEN event_name = 'event_3' THEN cd035
  END AS variable_3
FROM table_with_dirty_data

会占用我很长的时间,而且很容易出错。我想做的是使用 SELECT 语句 return 存储信息的 table_with_dirty_data 中的列名 (cd###) 并使用 WHILE 遍历所有不同的事件。因此,例如,使用 event_name = 'event_1' 它将是这样的:

SELECT 
  CASE
    WHEN event_name = 'event_1'
    THEN (SELECT variable_1 FROM matrix WHERE event_name = 'event_1')
  END AS variable_1
  , CASE
      WHEN event_name = 'event_1'
      THEN (SELECT variable_2 FROM matrix WHERE event_name = 'event_1')
    END AS variable_2
  , CASE
      WHEN event_name = 'event_1'
      THEN (SELECT variable_3 FROM matrix WHERE event_name = 'event_1')
    END AS variable_3
FROM table_with_dirty_data
WHERE event_name = 'event_1'

这里的目标是我可以循环遍历包含所有 event_name 的数组(这很容易实现)。最终我需要一个 table 中的所有事件,但我可以接受 table 来策划每个事件,只要它可以以编程方式完成(我个人不知道它是否均匀在 GBQ 中可能...必须检查一下)。

问题是我正在使用的 SELECT 语句被评估为一个字符串,因此 CASE 子句中的查询结果是字符串文字。例如,如果我为 event = 'event_1',

运行
SELECT variable_1 FROM matrix WHERE event_name = 'event_1'

评估为

'cd020'

然后导致外部查询变为

SELECT 
  CASE
    WHEN event_name = 'event_1' THEN 'cd020'
  END AS variable_1
  , CASE
      WHEN event_name = 'event_1' THEN 'cd035'
    END AS variable_2
  , CASE
      WHEN event_name = 'event_1' THEN 'cd120'
    END AS variable_3
FROM table_with_dirty_data
WHERE event_name = 'event_1'

这会产生这样的 table

event_name | variable_1 | variable_2 | variable_3
-----------|------------|------------|----------
'event_1'  | 'cd020'    | 'cd035'    | 'cd120'
'event_1'  | 'cd020'    | 'cd035'    | 'cd120'
'event_1'  | 'cd020'    | 'cd035'    | 'cd120'
每次找到事件 'event_1' 时,

而不是 return 存储在 cd020cd035cd120 列中的值。

有谁知道取消引用那些内部查询结果的方法,以便在执行外部查询时将它们转换为列名(因此 'cd020' 变为 cd020)??

PS:如果有人知道的话,我也愿意接受完全不同的策略。

听起来 EXECUTE IMMEDIATE 可能 会有所帮助。我不确定我是否完全理解映射,但它应该允许您根据 matrix table.

编写动态语句

但是,根据描述尚不清楚生成的查询可能有多笨重,因为您提到有数百种此类事件并且您可能会 运行 进入其他问题,例如查询复杂性或长度限制。

我按照@shollyman 的建议使用 EXECUTE IMMEDIATE 子句解决了这个问题。我只是为其中一个事件做了这件事,但我相信这回答了最初的问题(将它扩展到其他事件只是写一个 WHILE 循环的问题)。我会一步一步来,因为我没有使用实际的 repex。

第一步我用查询需要查找的事件的名称声明了一个名为 event 的变量。

DECLARE event STRING DEFAULT 'event_1';

然后我声明了一个变量,其中包含查询需要查找变量的列名。

DECLARE variable_name STRING DEFAULT (SELECT variable_1 FROM matrix WHERE event_name = event);

然后我像往常一样编写查询,但使用 EXECUTE IMMEDIATE 子句。我使用了三重双引号,以便可以将其分成多行以提高可读性)。

EXECUTE IMMEDIATE CONCAT("""
  SELECT
    CASE WHEN event_name = '""", event, "' THEN ", variable_name, """ END AS variable_1 
  FROM table_with_dirty_data
  WHERE event_name = '""", event, """'
""");

如果其他人要使用它,请注意我在一些三重双引号之前或之后使用的单独的单引号。我这样做是因为,例如,声明的变量 event,即使它是一个字符串,似乎也被连接为 event(周围没有单引号),这会中断查询执行。