Not everyone can use python nor pandas (think about dataAnalysts and BI dudes :) )
here is a dynamic pivot procedure in standard SQL@Bigquery.
it does not aggregate yet.
at first, you need to provide a table with already pe-KPI agregated values (if needed) .
but it automatically creates a table and generates all the pivoted columns.
the start hypothesis is that you have in input a table myDataset.myTable like this :
LONG,LAT,KPI,US,EUR
A,1,temp, 78,45
A,1,pressure, 120,114
B,1,temp,12,8
B,1,pressure, 85,52
if you call below procedure like this :
CALL warehouse.pivot ('myDataset','myTable',['LONG','LAT'], 'KPI');
you'll get a new table called myDataset.myTable_pivot like this :
LONG,LAT,temp_US,temp_EUR,pressure_US, pressure_EUR
A,1,78,45, 120, 114
B,1,12,8, 85, 52
here is the code :
create or replace procedure warehouse.pivot (dataset STRING, table_to_pivot STRING, ls_pks ARRAY<STRING>, pivot_column STRING)
BEGIN
DECLARE sql_pivot STRING;
DECLARE sql_pk_string STRING;
DECLARE sql_val_string STRING;
DECLARE sql_pivot_cols STRING DEFAULT "";
DECLARE pivot_cols_stmt STRING;
DECLARE pivot_ls_values ARRAY<STRING>;
DECLARE ls_pivot_value_columns ARRAY<STRING>;
DECLARE nb_pivot_col_values INT64;
DECLARE nb_pivot_val_values INT64;
DECLARE loop_index INT64 DEFAULT 0;
DECLARE loop2_index INT64 DEFAULT 0;
SET sql_pk_string= ( array_to_string(ls_pks,',') ) ;
SET pivot_cols_stmt = concat(
'SELECT array_agg(DISTINCT cast(', pivot_column ,' as string) ORDER BY ', pivot_column,' ) as pivot_ls_values, ',
'count(distinct ',pivot_column,') as nb_pivot_col_values ',
' FROM ', dataset,'.', table_to_pivot
);
EXECUTE IMMEDIATE pivot_cols_stmt into pivot_ls_values, nb_pivot_col_values;
set sql_val_string =concat(
"select array_agg(COLUMN_NAME) as ls_pivot_value_columns, count(distinct COLUMN_NAME) as nb_pivot_val_values ",
"FROM ",dataset,".INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='",table_to_pivot,"' ",
"and COLUMN_NAME not in ('",array_to_string(ls_pks,"','"),"', '",pivot_column,"')"
);
EXECUTE IMMEDIATE sql_val_string
into ls_pivot_value_columns, nb_pivot_val_values ;
while loop_index < nb_pivot_col_values DO
set loop2_index =0;
loop
SET sql_pivot_cols= concat (
sql_pivot_cols,
"max( ",
"if( ", pivot_column , "= '",pivot_ls_values[OFFSET (loop_index)],"' , ", ls_pivot_value_columns[OFFSET (loop2_index)], ", null) ",
") as ", pivot_ls_values[OFFSET (loop_index)], "_", ls_pivot_value_columns[OFFSET (loop2_index)],", "
);
SET loop2_index = loop2_index +1;
if loop2_index >= nb_pivot_val_values then
break;
end if;
END LOOP;
SET loop_index =loop_index+ 1;
END WHILE;
SET sql_pivot =concat (
"create or replace TABLE ", dataset,".",table_to_pivot,"_pivot as SELECT ",
sql_pk_string, ",", sql_pivot_cols, " FROM ",dataset,".", table_to_pivot ,
" GROUP BY ", sql_pk_string
);
EXECUTE IMMEDIATE sql_pivot;
END;
Strange things : nested while loops doesn't work in BQ. only the last while loop is executed. That's why in the procedure code you have a mix of WHILE and LOOP