5

i've serarched for the answer, but did't find.

so i've got a table types

CREATE TABLE types
(
  type_id serial NOT NULL,
  type_name character varying,
  CONSTRAINT un_type_name UNIQUE (type_name)
)

which holds type names, lets say users - and this is the name of corresponding table users. this design may be a bit ugly, but it was made to allow users create their own types. (is there better way to acheve this?)

now i want to perform a query like this one:

select type_name, (select count(*) from ???) from types

to get list of all type names and count of objects of each type.

can this be done?

2 Answers 2

6

You cannot do it directly in SQL

You can use a PLpgSQL function and dynamic SQL

CREATE OR REPLACE FUNCTION tables_count(OUT type_name character varying, OUT rows bigint)
RETURNS SETOF record AS $$
BEGIN
  FOR tables_count.type_name IN SELECT types.type_name FROM types
  LOOP
    EXECUTE 'SELECT COUNT(*) FROM ' || quote_ident(tables_count.type_name) INTO tables_count.rows;
    RETURN NEXT;
  END LOOP;
  RETURN;
END;
$$ LANGUAGE plpgsql;

SELECT * FROM tables_count();
Sign up to request clarification or add additional context in comments.

2 Comments

Ha, Pavel, you stole my thunder! Did not expect anybody else answering so soon. :) Obviously, I like your solution. You can tell that from what I came up with. Two small things: you were missing an OUT in the declaration and type_name is declared character varying which will raise an exception with your declared OUT parameter of type text. I fixed both.
thanks! i knew (or thought i knew) i can do it in plpgsql, i just thought how can it be done without it
5

I don't have enough information, but I do suspect that something is off with your design. You shouldn't need an extra table for every type.

Be that as it may, what you want to do cannot be done - in pure SQL. It can be done with a plpgsql function executing dynamic SQL, though:

CREATE OR REPLACE FUNCTION f_type_ct()
  RETURNS TABLE (type_name text, ct bigint) AS
$BODY$
DECLARE
    tbl     text;
BEGIN
    FOR tbl IN SELECT t.type_name FROM types t ORDER BY t.type_name
    LOOP
        RETURN QUERY EXECUTE
        'SELECT $1, count(*) FROM ' || tbl::regclass
        USING tbl;
    END LOOP;
END;
$BODY$
  LANGUAGE plpgsql;

Call:

SELECT * FROM f_type_ct();

You'll need to study most of the chapter about plpgsql in the manual to understand what's going on here.

One special hint: the cast to regclass is a safeguard against SQLi. You could also use the more generally applicable quote_ident() for that, but that does not properly handle schema-qualified table names, while the cast to regclass does. It also only accepts table names that are visible to the calling user.

2 Comments

great! i understand whats going on in this function :) i thought i could do this without functions. extra table for every type is needed for performance reasons - small tables can be indexed more effective afaik
@k102: BTW, indexes work just fine with big (or huge) tables. That's what they are built for. With small tables, sequential reads are often faster than using indexes at all.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.