CREATE OR REPLACE FUNCTION graficar(tabla text, columna text, valor text)
RETURNS SETOF resultado As
$func$
DECLARE
_query text;
limite text := '';
BEGIN
IF columna <> '' THEN
limite := format(' AND %I = %L', columna, valor); -- properly escaped
END IF;
_query :=
CASE tabla
WHEN 'edad' THEN
$q$WITH ranges AS (
SELECT concat(ten, '0-', ten, '9') AS range
,ten*10 AS r_min, ten*10+9 AS r_max
FROM generate_series(1,9) ten)
SELECT r.range AS nombre, count(p.*)::int AS cuenta
FROM ranges r
LEFT JOIN persona p ON p.edad BETWEEN r.r_min AND r.r_max$q$
|| limite || '
GROUP BY r.range
ORDER BY r.range'
WHEN 'ingreso' THEN
$q$WITH ranges AS (
SELECT concat(ten, '0-', ten*10 + 199, ' mil') AS range
,ten*10/1000 AS r_min, (ten*10+199999)/1000 AS r_max
FROM generate_series(0,(SELECT max(ingreso)/10000 FROM persona)
, 20) AS ten)
SELECT r.range AS nombre, count(p.*)::int AS cuenta
FROM ranges r
LEFT JOIN persona p ON p.ingreso BETWEEN r.r_min AND r.r_max$q$
|| limite || '
GROUP BY r.range
ORDER BY r.range'
ELSE
format(
$q$SELECT t.nombre, count(p.*)::int AS cuenta
FROM %1$I t
LEFT JOIN persona p on p.%1$I = t.nombre$q$ || limite || '
GROUP BY t.nombre'
, tabla)
END;
RETURN QUERY EXECUTE _query;
END
$func$ LANGUAGE plpgsql;
Major points:
Using
text
in place ofcharacter varying
for simplicity.Using
concat()
for easier formatting. Requires Postgres 9.1+.Use a human-readable format! The strings you posted can hardly be maintained.
Generating numbers starting from
1
for the first case, since you exclude the case for0
at the end anyway. Consequently, trim the now redundantHAVING
clause.The assignment operator in plpgsql is
:=
not=
- which generally works, but is an undocumented feature that may go away in future versions.Make proper use of dollar quotes.
Use a simple
RETURN QUERY
in place of the wholeLOOP
construct at the end.Don't use
query
as variable name, it is a reserved word in plpgsql. Substituted_query
instead.To avoid a potential type mismatch as described by @Daniel, provide
valor
as string literal in the query. This is a rare exception to the rule! Normally, the superior approach is to pass values with theUSING
clause like you had it. But to provide for a range of potentially varying types, your best choice is to supply an untyped string literal that can be coerced to any type automatically. This way, the expression remains sargable and any index potentially existing for the column can be used.Avoid SQL injection by properly escaping all identifiers and strings. I am using
format()
mostly. Requires Postgres 9.1+. Details in this related answer on dba.SE.