2016-12-03 71 views
0

我們剛剛開始將我們的查詢從Legacy遷移到標準SQL,因此我們正在學習如何處理嵌套數據和數組。錯誤:標量子查詢產生多個元素

基本上我們想要做的是從ga_sessions表檢索以下數據:

visitor id, session id, array of skus 
visitor 1, session 1, [sku_0, sku_1, (...), sku_n] 
visitor 1, session 2, [skus] 

要做到這一點,我們是把這個簡單的查詢:

WITH 
    customers_data AS(
    SELECT 
    fullvisitorid fv, 
    visitid v, 
    ARRAY_AGG((
     SELECT 
     prods.productsku 
     FROM 
     UNNEST(hits.product) prods)) sku 
    FROM 
    `dataset_id.ga_sessions_*`, 
    UNNEST(hits) hits 
    WHERE 
    1 = 1 
    AND _table_suffix BETWEEN FORMAT_DATE("%Y%m%d", DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)) 
    AND FORMAT_DATE("%Y%m%d", DATE_SUB(CURRENT_DATE(), INTERVAL 0 DAY)) 
    --and (select count(productsku) from unnest(hits.product) where productsku is not null) = 1 
    GROUP BY 
    fv, 
    v 
    LIMIT 
    100) 
SELECT 
    * 
FROM 
    customers_data 

但我們得到這個錯誤:

Error: Scalar subquery produced more than one element 

來自hits字段的數據看起來如此mething這樣的:

enter image description here

所以,當我們addded回where條款:

and (select count(productsku) from unnest(hits.product) where productsku is not null) = 1 

它不給一個錯誤,但結果有重複的SKU,我們也賠了進去越大的SKU陣列。

我們的查詢中是否存在一些錯誤,以防止數組未被引用?

回答

3

如果我理解正確的,我想你想是這樣的:

WITH customers_data AS (
    SELECT 
    fullvisitorid fv, 
    visitid v, 
    ARRAY_CONCAT_AGG(ARRAY(
     SELECT productsku FROM UNNEST(hits.product))) sku 
    FROM 
    `dataset_id.ga_sessions_*`, 
    UNNEST(hits) hits 
    WHERE 
    _table_suffix BETWEEN 
     FORMAT_DATE("%Y%m%d", DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)) 
     AND FORMAT_DATE("%Y%m%d", DATE_SUB(CURRENT_DATE(), INTERVAL 0 DAY)) 
    GROUP BY 
    fv, 
    v 
    LIMIT 
    100 
) 
SELECT 
    * 
FROM 
    customers_data; 

這會保留所有的SKU,通過一個多ARRAY子查詢提取的SKU爲每一行使用ARRAY_CONCAT_AGG。如果你想跨行重複數據刪除所有的SKU的,可以更換

SELECT 
    * 
FROM 
    customers_data; 

有:

SELECT * 
    REPLACE (ARRAY(SELECT DISTINCT s FROM UNNEST(sku) AS s) AS sku) 
FROM 
    customers_data; 

編輯:對於更多的閱讀,把文檔中看看types of expression subqueries。在你的情況下,你需要一個ARRAY子查詢,因爲想法是在每一行中取一個ARRAY<STRUCT<...>>,並將其轉換爲字段類型的ARRAY,以便跨行連接數組。

ARRAY_AGG從單個元素創建一個數組,而ARRAY_CONCAT_AGG從數組串聯創建一個數組。它們之間的區別類似於數組文字構造函數[]ARRAY_CONCAT之間的區別,但_AGG版本是集合函數。

作爲一個獨立的例子,可以嘗試:

WITH T AS (
    SELECT ARRAY<STRUCT<x INT64, y INT64>>[(1, 10), (2, 11), (3, 12)] AS arr UNION ALL 
    SELECT ARRAY<STRUCT<x INT64, y INT64>>[(4, 13)] UNION ALL 
    SELECT ARRAY<STRUCT<x INT64, y INT64>>[(5, 14), (6, 15)] 
) 
SELECT ARRAY(SELECT x FROM UNNEST(arr)) AS x_array 
FROM T; 

,其中每個陣列中的元件處於arr那些從每個元素x場這將返回一個列x_array。要連接所有數組,以便結果中有單行,請使用ARRAY_CONCAT_AGG,例如,:

WITH T AS (
    SELECT ARRAY<STRUCT<x INT64, y INT64>>[(1, 10), (2, 11), (3, 12)] AS arr UNION ALL 
    SELECT ARRAY<STRUCT<x INT64, y INT64>>[(4, 13)] UNION ALL 
    SELECT ARRAY<STRUCT<x INT64, y INT64>>[(5, 14), (6, 15)] 
) 
SELECT ARRAY_CONCAT_AGG(ARRAY(SELECT x FROM UNNEST(arr))) AS x_array 
FROM T; 

對於你的另一個問題,REPLACE接受與他們是爲了取代列配對錶達式的列表。表達式可以是簡單的,如文字,也可以是更復雜的東西,例如ARRAY子查詢,這就是我使用的。例如:

WITH T AS (
    SELECT 1 AS x, 'foo' AS y, true AS z UNION ALL 
    SELECT 2, 'bar', false UNION ALL 
    SELECT 3, 'baz', true 
) 
SELECT * REPLACE(1 - x AS x, CAST(x AS STRING) AS y) 
FROM T; 

這將替換會從SELECT *1 - xCAST(x AS STRING)而不是結果中返回原來的xy列。

+0

非常感謝埃利奧特的幫助!有效!請你解釋一下爲什麼我們之前做的是錯的?當我們對領域'productsku'進行了非常緊張並且然後使用'array_agg'時,它不應該起作用嗎?爲什麼我們需要將它轉換回'array'?另外,你能解釋一下'select * replace'語法嗎?我閱讀了關於它的文檔,但無法理解它在這個查詢中的工作方式。再一次,非常感謝! –

+0

我開始寫評論,但沒有足夠的空間,所以我更新了答案。如果您想了解更多關於主題的信息,請隨時發佈其他問題。謝謝! –

+0

哦,不,我看到它!現在我明白我做錯了什麼。非常感謝Elliott的幫助! –