I have a query that should create a view that will be used to be exported as csv file. Here's the query:
CREATE OR REPLACE VIEW coupons_export AS
SELECT
coupons.code AS coupon,
CONCAT('example.com/', programs.identifier, '/', coupons.code) AS url,
GROUP_CONCAT(items.name) AS items,
channels.name AS channel_name,
programs.name AS program_name,
SUM(coupon_stocks.current_quantity) AS current_quantity,
SUM(coupon_stocks.sent_quantity) AS sent_quantity
FROM
coupons
INNER JOIN
channels ON coupons.channel_id = channels.id
INNER JOIN
coupon_stocks ON coupons.id = coupon_stocks.coupon_id
INNER JOIN
items ON coupon_stocks.item_id = items.id
INNER JOIN
programs ON coupon_stocks.program_id = programs.id
WHERE
coupons.channel_id = 1
group by
coupon
The problem with this query is that MySQL throw an error saying that I need to add the programs.identifier to the GROUP BY, but I don't want to aggregate the view by that field. What I want to get as result is one row per 'coupon' and the related 'items' concatenated in one column as the following:
| coupon | url | items | channel_name | program_name | current_quantity | sent_quantity |
|---|---|---|---|---|---|---|
| CSV2WGQH | url.com/delivery/CSV2WGQH | item 1, item 2 | delivery | 3 | 0 | |
| CSVIFOQK | url.com/delivery/CSVIFOQK | item 1 | delivery | 1 | 1 | |
| CSV5KSDF | url.com/delivery/CSV5KSDF | item 1, item 2 | delivery | 3 | 3 | |
| CSVOSVAH | url.com/delivery/CSVOSVAH | item 1, item 2 | delivery | 3 | 3 |
but adding the programs.identifier to the GROUP BY what I get is
only two rows with all the items as the following:
| coupon | url | items | channel_name | program_name | current_quantity | sent_quantity |
|---|---|---|---|---|---|---|
| CSV2WGQH | url.com/delivery/CSV2WGQH | item 1, item 2, item 1, item 2, ... | delivery | 5050 | 0 | |
| CSVIFOQK | url.com/delivery/CSVIFOQK | item 1, item 1, item 1, item 1, ... | delivery | 5050 | 12 |
I could remove the GROUP BY, but this way I'll get an entrie for each item of each coupon, and I just want one entrie for coupon. Anyone can help me with that? I put an example DB at fiddle so you can try.
Thx
Solution
The accepted answer by @tinazmu and the comments by @Schwern, @ErgestBasha and @Eric helped me to undertand what was the problem with my query and how I should write it. The comment by @ErgestBasha have a working example of the query, although it won't work properly on fiddle, the query works fine with my RDBMS and my application. Here's the working query by @ErgestBasha:
CREATE OR REPLACE VIEW coupons_export AS
SELECT
coupons.code AS coupon,
MAX(CONCAT('example.com/', programs.identifier, '/', coupons.code)) AS url, ### Added MAX()
GROUP_CONCAT(items.name) AS items,
channels.name AS channel_name,
MAX(programs.name) AS program_name, -- Added MAX()
SUM(coupon_stocks.current_quantity) AS current_quantity,
SUM(coupon_stocks.sent_quantity) AS sent_quantity
FROM
coupons
INNER JOIN
channels ON coupons.channel_id = channels.id
INNER JOIN
coupon_stocks ON coupons.id = coupon_stocks.coupon_id
INNER JOIN
items ON coupon_stocks.item_id = items.id
INNER JOIN
programs ON coupon_stocks.program_id = programs.id
WHERE
coupons.channel_id = 1
group by
coupon
Thanks everyone for the help
You want to group by coupon: this means that you want one row per coupon.
You also want to see programs.identifier, channels.name, and programs.name. MySQL doesn't know what to do if it encounters more than one value of programs.identifier for a given coupon (excepting cases where MySQL infers functional dependencies, see the link by @Schwern).
If you know that these never change (ie. program.identifier is always the same for each coupon) then adding it to the GROUP BY is the right action. This requests one row per each combination of coupon and programs.identifier. On the other hand, if you know that the program.identifier can change for the same coupon and you still want to see one value of programs.identifier for each coupon, and you don't care which program.identifier is selected, simply add an aggregation function, like MIN/MAX around program.identifier.
Same consideration applies to channels.name, programs.name.