MySQL JSON_OBJECT instead of GROUP_CONCAT

SaidbakR picture SaidbakR · Feb 17, 2018 · Viewed 9k times · Source

I have the following sql query that works fine using GROUP_CONCAT:

SELECT orders.created_at,products.title, o_p.qty AS qty,

    (SELECT GROUP_CONCAT(features.title,"\:", o_p_f.value, features.unit) FROM order_product_features AS o_p_f 
     LEFT JOIN product_features ON product_features.id = o_p_f.product_feature_id
    LEFT JOIN features ON o_p_f.product_feature_id = product_features.id
     AND features.id = product_features.feature_id

     WHERE o_p_f.order_product_id = o_p.id
) AS prop
FROM orders
LEFT JOIN order_products AS o_p ON o_p.order_id = orders.id

LEFT JOIN products ON products.id = o_p.product_id

The above query returns result looks like the following screen shot:

enter image description here

Now, I want to replace GROUP_CONCAT with JSON_OBJECT or in other words, I want to have prop field to be JSON object. I have tried the following:

SELECT orders.created_at,products.title, o_p.qty AS qty,

    JSON_OBJECT((SELECT GROUP_CONCAT("title",features.title,"value", o_p_f.value, 'unit',features.unit) FROM order_product_features AS o_p_f 
     LEFT JOIN product_features ON product_features.id = o_p_f.product_feature_id
    LEFT JOIN features ON o_p_f.product_feature_id = product_features.id
     AND features.id = product_features.feature_id

     WHERE o_p_f.order_product_id = o_p.id
)) AS prop
FROM orders
LEFT JOIN order_products AS o_p ON o_p.order_id = orders.id

LEFT JOIN products ON products.id = o_p.product_id

However, the above query returns error:

1582 - Incorrect parameter count in the call to native function 'JSON_OBJECT'

Answer

Blag picture Blag · Feb 17, 2018

On ≥ 5.7.22

JSON_OBJECTAGG(key, value) is more likely what you're after.

mysql> SELECT o_id, attribute, value FROM t3;
+------+-----------+-------+
| o_id | attribute | value |
+------+-----------+-------+
|    2 | color     | red   |
|    2 | fabric    | silk  |
|    3 | color     | green |
|    3 | shape     | square|
+------+-----------+-------+
4 rows in set (0.00 sec)

mysql> SELECT o_id, JSON_OBJECTAGG(attribute, value) FROM t3 GROUP BY o_id;
+------+----------------------------------------+
| o_id | JSON_OBJECTAGG(attribute, name)        |
+------+----------------------------------------+
|    2 | {"color": "red", "fabric": "silk"}     |
|    3 | {"color": "green", "shape": "square"}  |
+------+----------------------------------------+
1 row in set (0.00 sec)

But as you don't provide your table structure I can't help you with the query.

It seem your handling of unit will grant you some extra work, as aggregate function use Key=>Value and will not take a third argument...

On ≥ 5.7

You'll have to make a bit of hand craft:

SELECT 
o_id, 
CONCAT(
    '{', 
    GROUP_CONCAT(
        TRIM(
            LEADING '{' FROM TRIM(
                TRAILING '}' FROM JSON_OBJECT(
                    `attribute`, 
                    `value`
                    )
                )
            )
        ),
    '}'
    ) json 
FROM t3 
GROUP BY o_id

≥ 5.5

Without any JSON function:

SELECT 
o_id, 
CONCAT(
    '{', 
    GROUP_CONCAT(
        CONCAT(
            '"',
            `attribute`,
            '":"',
            `value`,
            '"'
            )
        ),
    '}'
    ) json 
FROM t3 
GROUP BY o_id