Received: from localhost (postgresql.org [64.49.215.8]) by postgresql.org (Postfix) with ESMTP id 0F3C5475E18 for ; Thu, 7 Nov 2002 06:47:47 -0500 (EST) Received: from fzkmail2.fzk.de (fzkmail2.fzk.de [141.52.27.52]) by postgresql.org (Postfix) with SMTP id 105934759AF for ; Thu, 7 Nov 2002 06:47:46 -0500 (EST) Received: FROM resy5.fzk.de BY fzkmail2.fzk.de ; Thu Nov 07 12:47:47 2002 +0100 Received: œby rodos.fzk.de with ESMTP (8.8.6 (PHNE_17135)/8.7.1) id MAA06117; Thu, 7 Nov 2002 12:47:47 +0100 (MET) Message-ID: <3DCA52E2.5529E893@rodos.fzk.de> Date: Thu, 07 Nov 2002 12:47:46 +0100 From: Christoph Haller X-Mailer: Mozilla 4.06 [en] (X11; I; HP-UX B.10.20 9000/780) MIME-Version: 1.0 To: pgsql-sql@postgresql.org Cc: terry@ashtonwoodshomes.com, chaudhar@umich.edu Subject: Generating a cross tab (pivot table) Content-Type: text/plain; charset=iso-8859-2 Content-Transfer-Encoding: 7bit X-Virus-Scanned: by AMaViS new-20020517 X-Archive-Number: 200211/90 X-Sequence-Number: 10232 Regarding to Terry's request on multiple aggregates and Shahbaz's request for generating a cross tab ( pivot table ) in September, I've found an excellent example on a german inet page http://www.itrain.de/ I've translated it and think it's useful for many who subscribed (I hope so, maybe it was told before, but I couldn't find anything about this topic in the techdecs). Objective: There is a relation "sales", holding the sales of different products of different vendors. The task is to generate a report which shows the sales of every vendor and every product. Consider the following table populated with some data: CREATE TABLE sales ( product TEXT, vendor TEXT, sales INTEGER ); INSERT INTO sales VALUES ( 'milk' , 'mr. pink' , 12 ) ; INSERT INTO sales VALUES ( 'milk' , 'mr. brown' , 8 ) ; INSERT INTO sales VALUES ( 'honey' , 'mr. green' , 2 ) ; INSERT INTO sales VALUES ( 'milk' , 'mr. green' , 34 ) ; INSERT INTO sales VALUES ( 'butter', 'mr. pink' , 17 ) ; INSERT INTO sales VALUES ( 'butter', 'mr. brown' , 2 ) ; INSERT INTO sales VALUES ( 'honey' , 'mr. pink' , 19 ) ; The following query generates the report: SELECT product, SUM(CASE vendor WHEN 'mr. pink' THEN sales ELSE 0 END) AS "mr. pink ", SUM(CASE vendor WHEN 'mr. brown' THEN sales ELSE 0 END) AS "mr. brown", SUM(CASE vendor WHEN 'mr. green' THEN sales ELSE 0 END) AS "mr. green", SUM(sales) AS "sum of sales" FROM sales GROUP BY product ; product | mr. pink | mr. brown | mr. green | sum of sales ---------+-----------+-----------+-----------+-------------- butter | 17 | 2 | 0 | 19 honey | 19 | 0 | 2 | 21 milk | 12 | 8 | 34 | 54 (3 rows) The example is based on MS SQL Server 7.0 and it appears to be there is a valuable feature called CUBE which completes the report. SELECT CASE WHEN GROUPING(product) = 1 THEN 'sum of sales' ELSE product END, SUM(CASE vendor WHEN 'mr. pink' THEN sales ELSE 0 END) AS "mr. pink ", SUM(CASE vendor WHEN 'mr. brown' THEN sales ELSE 0 END) AS "mr. brown", SUM(CASE vendor WHEN 'mr. green' THEN sales ELSE 0 END) AS "mr. green", SUM(sales) AS "sum of sales" FROM sales GROUP BY product WITH CUBE ; product | mr. pink | mr. brown | mr. green | sum of sales --------------+-----------+-----------+-----------+-------------- butter | 17 | 2 | 0 | 19 honey | 19 | 0 | 2 | 21 milk | 12 | 8 | 34 | 54 sum of sales | 48 | 10 | 36 | 94 (4 rows) I would like to hear from the core team whether they think this feature is worthy to be implemented, or even better, is there a similar one or an easy workaround already. It's obvious this approach is most inflexible. As soon as there is a new vendor, one has to re-write the query and add SUM(CASE vendor WHEN 'mr. new' THEN ... , In an advanced example it is shown how to deal with cross tabs in general using a stored procedure. I am going to translate this and re-write it for postgres, too (ok, I will try). Regards, Christoph