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:
- Create a CTE with product rankings by brand and left join the CTE with itself ten times, filtering by rank on each join.
- 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?