Simplify what you have:
DO
$do$
DECLARE
_param text := 'last'; -- one can assign at declaration time
results resultrow[];
BEGIN
results := ARRAY(
SELECT t::resultrow -- refer to table alias to get whole row
FROM (
SELECT CASE _param -- simple "switched" CASE
WHEN 'first' THEN fname
WHEN 'last' THEN lname
END
,sum(salary)
FROM example
GROUP BY 1 -- simpler with positional reference
) t
);
RAISE NOTICE '%', results;
END
$do$ LANGUAGE plpgsql;
Using simple CASE
syntax variant. This way the expression is only evaluated once and the syntax is simpler. Since your question refers to CASE
- even if that's hardly relevant.
Also using a positional reference in the GROUP BY
clause. This seems relevant to the title of your question. More explanation in these related answers:
Select first row in each GROUP BY group?
GROUP BY + CASE statement
This kind of query can be very inefficient. It's not a problem of the (very cheap!) CASE
statement per se. It's because the planner has to provide for varying input in the first column and may be forced to use a generic, less optimized plan.
Dynamic SQL
I assume the actual goal is to write a function that takes my_parameter
. Use dynamic SQL with EXECUTE
, which will likely result in a superior query plan, i.e. superior performance. There are lots of code example here, try a search.
Also, I return a set of resultrow
instead of the awkward ARRAY
you had in your example (since you cannot return from a DO
statement):
CREATE FUNCTION f_salaray_for_param(_param text)
RETURNS SETOF resultrow AS
$func$
DECLARE
_fld text :=
CASE _param
WHEN 'first' THEN 'fname' -- SQL injection not possible
WHEN 'last' THEN 'lname'
END;
BEGIN
IF _fld IS NULL THEN -- exception for invalid params
RAISE EXCEPTION 'Unexpected value for _param: %', _param;
END IF;
RETURN QUERY EXECUTE '
SELECT ' || _fld || ', sum(salary)
FROM example
GROUP BY 1'; -- query is very simple now
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM f_salaray_for_param('first');
BTW, the plpgsql assignment operator is :=
(not =
).