Skip to content
Advertisement

MySQL – GROUP BY not working with multiple CASE WHEN statements

I am working on a database with Kickstarter database, and I’m trying to:

  1. Define short, medium-lengthed, and long campaigns
  2. See how much each length of campaign raise
  3. Convert different currencies into USD

My original code is as follows:

 SELECT 
    CASE 
        WHEN DATEDIFF(campaign.deadline,campaign.launched)<=30 THEN 'Short Campaign' 
        WHEN DATEDIFF(campaign.deadline,campaign.launched)>30 AND DATEDIFF(campaign.deadline,campaign.launched)<=60 THEN 'Mid-lengthed Campaign'
        ELSE 'Long Campaign'
        END AS 'Campaign Length',
    CASE 
        WHEN currency.name='GBP' THEN ROUND(SUM(campaign.pledged)*0.80,2)
        WHEN currency.name='CAD' THEN ROUND(SUM(campaign.pledged)*1.36)
        WHEN currency.name='AUD' THEN ROUND(SUM(campaign.pledged)*1.43)
        WHEN currency.name='NOK' THEN ROUND(SUM(campaign.pledged)*9.28)
        WHEN currency.name='EUR' THEN ROUND(SUM(campaign.pledged)*0.87)
        WHEN currency.name='MXN' THEN ROUND(SUM(campaign.pledged)*22.48)
        WHEN currency.name='SEK' THEN ROUND(SUM(campaign.pledged)*0.04)
        WHEN currency.name='NZD' THEN ROUND(SUM(campaign.pledged)*1.53)
        WHEN currency.name='CHF' THEN ROUND(SUM(campaign.pledged)*0.94)
        WHEN currency.name='DKK' THEN ROUND(SUM(campaign.pledged)*6.52)
        WHEN currency.name='HKD' THEN ROUND(SUM(campaign.pledged)*7.75)
        WHEN currency.name='SGD' THEN ROUND(SUM(campaign.pledged)*1.39)
        WHEN currency.name='JPY' THEN ROUND(SUM(campaign.pledged)*107.11)
        ELSE ROUND(SUM(campaign.pledged),2)
    END AS 'Amount Raised'
FROM campaign
LEFT JOIN currency ON currency.id=campaign.currency_id
GROUP BY `Campaign Length`;

I’m expecting a result of:

Campaign Length.      ---- Amount Raised
Short Campaign.       ---- XXXXXXXXXXXXX
Mid-Lengthed Campaign ---- XXXXXXXXXXXXX
Long Campaign.        ---- XXXXXXXXXXXXX

Where XXXXXX is the aggregate of the pledged amount after conversion. The original code shows:

Error Code: 1055. Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'ks_data.currency.name' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

So I improvised by adding additional GROUP BY, after realizing I can’t GROUP BY Amount Raised:

 SELECT 
    CASE 
        WHEN DATEDIFF(campaign.deadline,campaign.launched)<=30 THEN 'Short Campaign' 
        WHEN DATEDIFF(campaign.deadline,campaign.launched)>30 AND DATEDIFF(campaign.deadline,campaign.launched)<=60 THEN 'Mid-lengthed Campaign'
        ELSE 'Long Campaign'
        END AS 'Campaign Length',
    CASE 
        WHEN currency.name='GBP' THEN ROUND(SUM(campaign.pledged)*0.80,2)
        WHEN currency.name='CAD' THEN ROUND(SUM(campaign.pledged)*1.36)
        WHEN currency.name='AUD' THEN ROUND(SUM(campaign.pledged)*1.43)
        WHEN currency.name='NOK' THEN ROUND(SUM(campaign.pledged)*9.28)
        WHEN currency.name='EUR' THEN ROUND(SUM(campaign.pledged)*0.87)
        WHEN currency.name='MXN' THEN ROUND(SUM(campaign.pledged)*22.48)
        WHEN currency.name='SEK' THEN ROUND(SUM(campaign.pledged)*0.04)
        WHEN currency.name='NZD' THEN ROUND(SUM(campaign.pledged)*1.53)
        WHEN currency.name='CHF' THEN ROUND(SUM(campaign.pledged)*0.94)
        WHEN currency.name='DKK' THEN ROUND(SUM(campaign.pledged)*6.52)
        WHEN currency.name='HKD' THEN ROUND(SUM(campaign.pledged)*7.75)
        WHEN currency.name='SGD' THEN ROUND(SUM(campaign.pledged)*1.39)
        WHEN currency.name='JPY' THEN ROUND(SUM(campaign.pledged)*107.11)
        ELSE ROUND(SUM(campaign.pledged),2)
    END AS 'Amount Raised'
FROM campaign
LEFT JOIN currency ON currency.id=campaign.currency_id
GROUP BY `Campaign Length`,currency.id;

But the result is instead not grouping as I intended:

Campaign Length.      ---- Amount Raised
Short Campaign.       ---- XXXXXXXXXXXXX
Mid-Lengthed Campaign ---- XXXXXXXXXXXXX
Long Campaign.        ---- XXXXXXXXXXXXX
Mid-Lengthed Campaign ---- XXXXXXXXXXXXX
Mid-Lengthed Campaign ---- XXXXXXXXXXXXX
Short Campaign.       ---- XXXXXXXXXXXXX

I’ve tried many ways, but couldn’t find a solution

Advertisement

Answer

You need to move the case expression on the currency name within the aggregate function:

SELECT 
    CASE 
        WHEN DATEDIFF(ca.deadline,ca.launched) <= 30 THEN 
            'Short Campaign' 
        WHEN DATEDIFF(ca.deadline,ca.launched) <= 60 
            THEN 'Mid-lengthed Campaign'
        ELSE 'Long Campaign'
    END AS campaign_length,
    ROUND(SUM(
        ca.pledged 
        * CASE cu.name
            WHEN 'GBP' THEN 0.8
            WHEN 'CAD' THEN 1.36
            WHEN 'AUD' THEN 1.43
            ...
            ELSE 1
        END
    ), 2) as amount_raised
FROM campaign ca
LEFT JOIN currency cu ON cu.id = ca.currency_id 
GROUP BY campaign_length;

Other things to note:

  • don’t use single quotes for column identifiers – they are meant for literal strings (MySQL wants backticks instead); matter of fact, it is just simpler to use identifiers that do not require quoting

  • both case expressions can be simplified, as shown above; in the first one, there is no need to check that the duration of the campaign is greater than 30 in the second branch (the first branch traps that already); in the second one, you can use the short-circuit form instead of repeating the column name in each branch

  • table aliases make the query easier to write and read

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement