Query to display aggregate information of several items on single line

Viewed 23

I'm having trouble trying to display aggregate data from a bigquery table into a single line.

I have a table containing information like brand_id, brand_name, product_id, product_name, product_price, product_id, product_description, product_sales, positive_reviews and negative_reviews:

brand_id brand_name product_id product_name product_description product_price product_sales positive_reviews negative_reviews
a brand a Prd_a_1 Product A 1 Lorem ipsum dolor sit amet 1.99 99 60 39
b brand b Prd_b_1 Product B 1 Lorem ipsum dolor sit amet 2 153 100 53
c brand c Prd_b_1 Product C 1 Lorem ipsum dolor sit amet 3.50 56 20 36
d brand d Prd_b_1 Product D 1 Lorem ipsum dolor sit amet 198.51 2 1 1
e brand e Prd_b_1 Product E 1 Lorem ipsum dolor sit amet 17.70 45 25 20
... ... ... ... ... ... ... ... ...
a brand a Prd_a_99 Product A 99 Morbi viverra lacinia elementum 34 36 18 18
b brand b Prd_b_99 Product B 99 Morbi viverra lacinia elementum 71 91 61 30
c brand c Prd_c_99 Product C 99 Morbi viverra lacinia elementum 91 61 40 21
d brand d Prd_d_99 Product D 99 Morbi viverra lacinia elementum 123 3 2 1
e brand e Prd_e_99 Product E 99 Morbi viverra lacinia elementum 489 1 1 0

What I want to do is display the information of the top 10 best selling products of each brand, having one row for each brand. Like so:

brand_id brand_name t1_product_id t1_product_name t1_product_description t1_product_price t1_product_sales t1_product_positive_reviews t1_product_negative_reviews ... t10_product_id t10_product_name t10_product_description t10_product_price t10_product_sales t10_product_positive_reviews t10_product_negative_reviews
a brand a Prd_a_1 Product A 1 Lorem ipsum dolor sit amet 1.99 99 60 39 ... Prd_a_16 Product A 16 Lorem ipsum lacinia elementum 16.99 80 55 25
b brand b Prd_b_1 Product B 1 Lorem ipsum dolor sit amet 2 153 100 53 ... Prd_b_31 Product B 31 Lorem ipsum lacinia elementum 6.99 100 70 30
c brand c Prd_c_1 Product C 1 Lorem ipsum dolor sit amet 3.50 112 60 52 ... Prd_c_24 Product C 24 Lorem ipsum lacinia elementum 8.80 30 20 10
d brand d Prd_d_1 Product D 1 Lorem ipsum dolor sit amet 1.99 241 120 121 ... Prd_d_37 Product D 37 Lorem ipsum lacinia elementum 1.45 140 70 70
e brand e Prd_e_1 Product E 1 Lorem ipsum dolor sit amet 17.70 70 60 10 ... Prd_e_2 Product E 2 Lorem ipsum lacinia elementum 23 40 45 25

I have tried two approeaches so far:

  1. Create a CTE with product rankings by brand and left join the CTE with itself ten times, filtering by rank on each join.
  2. Create a CTE with product rankings by brand and pivot results.

OPTION 1

WITH RANKED_PRODUCTS
AS
(
    SELECT
        BRAND_ID,
        BRAND_NAME,
        PRODUCT_ID,
        PRODUCT_NAME,
        PRODUCT_DESCRIPTION,
        PRODUCT_PRICE,
        PRODUCT_SALES,
        POSITIVE_REVIEWS,
        NEGATIVE_REVIEWS,
        RANK() OVER(PARTITION BY BRAND_ID ORDER BY PRODUCT_SALES DESC) AS RANK
    FROM PRODUCTS
)
SELECT
    RP.BRAND_ID,
    RP.BRAND_NAME,
    RP.PRODUCT_ID,
    RP.PRODUCT_NAME,
    RP.PRODUCT_DESCRIPTION,
    RP.PRODUCT_PRICE,
    RP.PRODUCT_SALES,
    RP.POSITIVE_REVIEWS,
    RP.NEGATIVE_REVIEWS,
    RP2.BRAND_ID,
    RP2.BRAND_NAME,
    RP2.PRODUCT_ID,
    RP2.PRODUCT_NAME,
    RP2.PRODUCT_DESCRIPTION,
    RP2.PRODUCT_PRICE,
    RP2.PRODUCT_SALES,
    RP2.POSITIVE_REVIEWS,
    RP2.NEGATIVE_REVIEWS,
    (...)
    RP10.BRAND_ID,
    RP10.BRAND_NAME,
    RP10.PRODUCT_ID,
    RP10.PRODUCT_NAME,
    RP10.PRODUCT_DESCRIPTION,
    RP10.PRODUCT_PRICE,
    RP10.PRODUCT_SALES,
    RP10.POSITIVE_REVIEWS,
    RP10.NEGATIVE_REVIEWS
FROM RANKED_PRODUCTS RP
LEFT JOIN (
SELECT BRAND_ID, PRODUCT_ID, PRODUCT_NAME, PRODUCT_DESCRIPTION, PRODUCT_PRICE, PRODUCT_SALES, POSITIVE_REVIEWS, NEGATIVE_REVIEWS
FROM RANKED_PRODUCTS WHERE RANK = 2
) RP2 USING (BRAND_ID)
(...)
LEFT JOIN (
SELECT BRAND_ID, PRODUCT_ID, PRODUCT_NAME, PRODUCT_DESCRIPTION, PRODUCT_PRICE, PRODUCT_SALES, POSITIVE_REVIEWS, NEGATIVE_REVIEWS
FROM RANKED_PRODUCTS WHERE RANK = 10
) RP10 USING (BRAND_ID)
WHERE RANK = 1

This option works, but the query takes a long time and honestly it doesn't fell right. There must be a better way.

OPTION 2

SELECT
    *
FROM (
SELECT
    BRAND_ID,
    BRAND_NAME,
    PRODUCT_ID,
    PRODUCT_NAME,
    PRODUCT_DESCRIPTION,
    PRODUCT_PRICE,
    PRODUCT_SALES,
    POSITIVE_REVIEWS,
    NEGATIVE_REVIEWS,
    RANK() OVER(PARTITION BY BRAND_ID ORDER BY PRODUCT_SALES DESC) AS RANK
FROM PRODUCTS
) PIVOT (
    MAX(PRODUCT_DESCRIPTION) AS PRODUCT_DESCRIPTION,
    MAX(PRODUCT_PRICE) AS PRODUCT_PRICE,
    MAX(PRODUCT_SALES) AS PRODUCT_SALES,
    MAX(POSITIVE_REVIEWS) AS POSITIVE_REVIEWS, 
    MAX(NEGATIVE_REVIEWS) AS NEGATIVE_REVIEWS
    FOR RANK IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10))

This options feels a lot better, and seems to be on the right track. Unfortunately the output is not what I desire:

brand_id brand_name product_id product_name t1_product_description t1_product_price t1_product_sales t1_product_positive_reviews t1_product_negative_reviews ... t10_product_description t10_product_price t10_product_sales t10_product_positive_reviews t10_product_negative_reviews
a brand a Prd_a_1 Product A 1 Lorem ipsum dolor sit amet 1.99 99 60 39 ... null null null null
a brand a Prd_a_16 Product A 16 null null null null null ... Morbi viverra lacinia elementum 6.99 100 70 30
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
e brand e Prd_e_1 Product E 1 Lorem ipsum dolor sit amet 1.99 241 120 121 ... null null null null
e brand e Prd_e_2 Product E 2 null null null null null ... Morbi viverra lacinia elementum 23 40 45 25

I do know that I could add another query on top of this one in order to have all results on a single line, but again this feels kind of wrong.


Any ideas on what could be done?

0 Answers
Related