sqlsql-servercount

Add column count of occurrences


I have table in SQL Server called df found here:

-- Parameters
DECLARE @Year INT = 2020; --, @Country varchar(50)= 'Brazil';

WITH ModeData AS (
    SELECT country, 
           a.Mode
    FROM df
    CROSS APPLY (
        SELECT TOP 1 Mode, COUNT(*) AS cnt
        FROM (VALUES (val1), (val2), (val3)) AS t(Mode)
        GROUP BY Mode
        ORDER BY COUNT(*) DESC
    ) a
  where year=@year --and  country=@country 
)

-- Calculate proportions and map modes to labels
, Proportions AS (
    SELECT country, 
           CASE 
               WHEN Mode = 1 THEN 'Very Dissatisfied'
               WHEN Mode = 2 THEN 'Dissatisfied'
               WHEN Mode = 3 THEN 'Neutral'
               WHEN Mode = 4 THEN 'Satisfied'
               WHEN Mode = 5 THEN 'Very Satisfied'
           END AS SatisfactionLevel,
           COUNT(*) * 1.0 / SUM(COUNT(*)) OVER (PARTITION BY country) AS Proportion
    FROM ModeData
    GROUP BY country, Mode
)

-- Pivot the results to get each satisfaction level as a column
SELECT country, 
       [Very Dissatisfied], 
       [Dissatisfied], 
       [Neutral], 
       [Satisfied], 
       [Very Satisfied]
FROM Proportions
PIVOT (
    MAX(Proportion)
    FOR SatisfactionLevel IN ([Very Dissatisfied], [Dissatisfied], [Neutral], [Satisfied], [Very Satisfied])
) AS p
ORDER BY country;

The resulted table is :

Country Very Dissatisfied Dissatisfied Neutral Satisfied Very Satisfied
Brazil 0.285714285714 0.142857142857 0.142857142857 0.142857142857 0.285714285714
Canada 0.111111111111 0.111111111111 0.333333333333 0.222222222222 0.222222222222
France 0.250000000000 0.125000000000 0.250000000000 0.250000000000 0.125000000000
Italy 0.166666666666 0.166666666666 0.166666666666 0.166666666666 0.333333333333
USA 0.222222222222 0.111111111111 0.111111111111 0.333333333333 0.222222222222

I want to calculate the count of each country.How many rows has each country in the table df and add this count as an extra column in the resulted table. Ideally and based on the toy example data the result I want to look like:

Country Very Dissatisfied Dissatisfied Neutral Satisfied Very Satisfied Count
Brazil 0.285714285714 0.142857142857 0.142857142857 0.142857142857 0.285714285714 7
Canada 0.111111111111 0.111111111111 0.333333333333 0.222222222222 0.222222222222 9
France 0.250000000000 0.125000000000 0.250000000000 0.250000000000 0.125000000000 8
Italy 0.166666666666 0.166666666666 0.166666666666 0.166666666666 0.333333333333 6
USA 0.222222222222 0.111111111111 0.111111111111 0.333333333333 0.222222222222 9

Solution

  • You are way over-complicating it. Instead of calculating each SatisfactionLevel as a separate group, and then having to pivot it back, just use conditional aggregation.

    -- Parameters
    DECLARE @Year INT = 2020; --, @Country varchar(50)= 'Brazil';
    
    WITH ModeData AS (
        SELECT country, 
               a.Mode
        FROM df
        CROSS APPLY (
            SELECT TOP 1 Mode, COUNT(*) AS cnt
            FROM (VALUES (val1), (val2), (val3)) AS t(Mode)
            GROUP BY Mode
            ORDER BY COUNT(*) DESC
        ) a
        where year = @year --and  country=@country 
    )
    
    SELECT
      country, 
      COUNT(CASE WHEN Mode = 1 THEN 1 END) * 1.0 / COUNT(*) AS [Very Dissatisfied], 
      COUNT(CASE WHEN Mode = 2 THEN 1 END) * 1.0 / COUNT(*) AS [Dissatisfied], 
      COUNT(CASE WHEN Mode = 3 THEN 1 END) * 1.0 / COUNT(*) AS [Neutral],
      COUNT(CASE WHEN Mode = 4 THEN 1 END) * 1.0 / COUNT(*) AS [Satisfied], 
      COUNT(CASE WHEN Mode = 5 THEN 1 END) * 1.0 / COUNT(*) AS [Very Satisfied],
      COUNT(*) AS Count
    FROM ModeData
    GROUP BY
      country
    ORDER BY
      country;
    

    db<>fiddle