Re: Subquery for column names of tablefunc crosstab queries

From: Joe Conway <mail(at)joeconway(dot)com>
To: Julia Jacobson <julia(dot)jacobson(at)arcor(dot)de>
Cc: pgsql-general(at)postgresql(dot)org
Subject: Re: Subquery for column names of tablefunc crosstab queries
Date: 2011-01-23 01:05:07
Message-ID: 4D3B7EC3.9030108@joeconway.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general

On 01/21/2011 09:34 AM, Julia Jacobson wrote:
> Two further problems are the fact that the names of columns are not
> allowed to begin with a number and every entry in the table definition
> list must not only contain the name of the column but of course also a
> data type (always the same - int).
> Is it possible for a newbie to solve my problem by a user-defined
> function in PL/pgSQL or is it rather complicated?

It's a bit tricky to get correct. The following is a bit ugly, and just
barely tested, but works at least for your presented case:

-------------------------------------------
CREATE OR REPLACE FUNCTION generate_crosstab_sql(relname text,
grpattr text,
grpattrtyp text,
catattr text,
valattr text,
valattrtyp text,
whereclause text)
RETURNS text AS $$
DECLARE
crosstabsql text;
coldef text;
catdef text;
rec record;
BEGIN
coldef := '(' || grpattr || ' ' || grpattrtyp;
catdef := 'VALUES';
FOR rec IN EXECUTE
'SELECT DISTINCT ' || catattr ||
' AS c FROM ' || relname || ' WHERE ' ||
whereclause || ' ORDER BY 1'
LOOP
coldef := coldef || ',"' || rec.c || '" ' || valattrtyp;
IF catdef = 'VALUES' THEN
catdef := catdef || '($v$' || rec.c || '$v$)';
ELSE
catdef := catdef || ',($v$' || rec.c || '$v$)';
END IF;
END LOOP;
coldef := coldef || ')';

IF catdef != 'VALUES' THEN
crosstabsql :=
$ct$SELECT * FROM crosstab ('SELECT $ct$ ||
grpattr || $ct$,$ct$ ||
catattr || $ct$,$ct$ || valattr ||
$ct$ FROM $ct$ || relname ||
$ct$ WHERE $ct$ || whereclause ||
$ct$ ORDER BY 1,2','$ct$ ||
catdef || $ct$') AS $ct$ || coldef;
END IF;
RETURN crosstabsql;
END;
$$ LANGUAGE plpgsql;
-------------------------------------------

Then this call:
-------------------------------------------
SELECT generate_crosstab_sql('mytable',
'rowid',
'text',
'rowdt::date',
'temperature',
'int',
'1 = 1');
-------------------------------------------

Produces this SQL:
-------------------------------------------
SELECT * FROM crosstab ('SELECT rowid,rowdt::date,temperature FROM
mytable WHERE 1 = 1 ORDER BY
1,2','VALUES($v$2003-03-01$v$),($v$2003-03-02$v$),($v$2003-03-03$v$)')
AS (rowid
text,"2003-03-01" int,"2003-03-02" int,"2003-03-03" int)
-------------------------------------------

Which produces this result:
-------------------------------------------
SELECT * FROM crosstab ('SELECT rowid,rowdt::date,temperature FROM
mytable WHERE 1 = 1 ORDER BY
1,2','VALUES($v$2003-03-01$v$),($v$2003-03-02$v$),($v$2003-03-03$v$)')
AS (rowid
contrib_regression(# text,"2003-03-01" int,"2003-03-02" int,"2003-03-03"
int);
rowid | 2003-03-01 | 2003-03-02 | 2003-03-03
-------+------------+------------+------------
test1 | 42 | |
test2 | | 53 |
test3 | | | 49
(3 rows)
-------------------------------------------

You might need some adjustments to get this to do exactly what you want.
Also please test it for correctness ;-)

HTH,

Joe

--
Joe Conway
credativ LLC: http://www.credativ.us
Linux, PostgreSQL, and general Open Source
Training, Service, Consulting, & 24x7 Support

In response to

Responses

Browse pgsql-general by date

  From Date Subject
Next Message Arturo Perez 2011-01-23 05:30:51 temporal period type and select distinct gives equality error
Previous Message D M 2011-01-23 00:09:46 Re: SHMMAX and SHMALL question