Insert trigger function from select query, plus static values pulled from the new.
Your problem is that your query is using new.columnname to mean different things.

In the SELECT list of the subquery, the first new.columnname is intended to use the actual value stored new.columnname. The second new.columnname, the one in the COUNT() and the one in the GROUP BY are all intended not to be the literal value stored in new.columnname, but a reference to the actual OrderDetails column named in new.columnname. In other words, the first is the string literal "format", while the second is the database column OrderDetails.format.

Unfortunately, the engine will view each of new.columnname in the query as a string value "format", and will never interpret it as OrderDetails.format.

There are at least two potential ways to get around this problem:

Explicit INSERTs for each column

For each of the columns in OrderDetails, write a separate INSERT statement in your trigger:

if old.isdropdown &lt;&gt; new.isdropdown then --if there was a change
  if new.isdropdown = true then --and the change was from false to true
    if new.columnname = 'first_column' then
      insert into dropdownstats 
      (orderid,columnname,columnvalues,countofcolumnvalues) 
        select 
        orderid
        , 'first_column'
        , first_column
        , count(*)
        from orderdetails
        group by orderid, first_column; 
    elsif new.columnname = 'format' then
      insert into dropdownstats 
      (orderid,columnname,columnvalues,countofcolumnvalues) 
        select 
        orderid
        , 'format'
        , format
        , count(*)
        from orderdetails
        group by orderid, format; 
    elsif ...
    end;
  else --the transition was from true to false
    delete from dropdownstats as dd where dd.columnname = new.columnname;
  end if;
end if;


(If there's special handling you want to do if new.columnname doesn't match any of the values, you can use CASE ... WHEN ... ELSE ... END; instead of IF ... THEN ... ELSIF ... THEN ... END;, if you like.)

Dynamic SQL

Unless PostgreSQL has some restriction against it, you could use dynamic SQL to build the SQL statement you need each time through:

if old.isdropdown &lt;&gt; new.isdropdown then --if there was a change
  if new.isdropdown = true then --and the change was from false to true
    EXECUTE format( 'insert into dropdownstats '
                    '(orderid,columnname,columnvalues,countofcolumnvalues) '
                      'select '
                      'orderid '
                      ', %L '
                      ', %I '
                      ', count(*) '
                      'from orderdetails '
                      'group by orderid, %I '
                   ,new.columnname, new.columnname, new.columnname);
  else --the transition was from true to false
    delete from dropdownstats as dd where dd.columnname = new.columnname;
  end if;
end if;


CAVEATS: Code is untested, and PostgreSQL is not my primary SQL language, so I may have included typos or misunderstood some subtleties.