Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to use Common Table Expressions in Postgres Crosstab Query

I'm trying to use perform a pivot operation on some data using Postgres' tablefunc extension's CROSSTAB function. The data needs to undergo some transformations first, which I perform in some common table expressions.

However, it appears CROSSTAB can't see the results of those expressions.

For example, this query sourcing data from a temporary table works fine:

CREATE TEMPORARY TABLE
  temporary_table
    (name, category, category_value)
ON COMMIT DROP
AS (
  VALUES
    ('A',  'foo',    1             ),
    ('A',  'bar',    2             ),
    ('B',  'foo',    3             ),
    ('B',  'bar',    4             )
);

SELECT * FROM
  CROSSTAB(
    'SELECT * FROM temporary_table',
    $$
      VALUES
        ('foo'),
        ('bar')
    $$
  ) AS (
    name TEXT,
    foo  INT,
    bar  INT
  );

and, as expected, produces the following output:

name | foo     | bar
text | integer | integer
---- | ------- | -------
A    |       1 |       2
B    |       3 |       4

But the same query, this time using common table expressions doesn't run:

WITH
  common_table
    (name, category, category_value)
AS (
  VALUES
    ('A',  'foo',    1             ),
    ('A',  'bar',    2             ),
    ('B',  'foo',    3             ),
    ('B',  'bar',    4             )
)
SELECT * FROM
  CROSSTAB(
    'SELECT * FROM common_table',
    $$
      VALUES
        ('foo'),
        ('bar')
    $$
  ) AS (
    name TEXT,
    foo  INT,
    bar  INT
  )

and produces the following error:

ERROR:  relation "common_table" does not exist
LINE 1: SELECT * FROM common_table
                      ^
QUERY:  SELECT * FROM common_table

********** Error **********

ERROR: relation "common_table" does not exist
SQL state: 42P01

I take it this means the text query (SELECT * FROM common_table) runs in some sort of different context?


Note: The tablefunc extension must be enabled for CROSSTAB to be available:

CREATE EXTENSION IF NOT EXISTS tablefunc;
like image 810
bosticko Avatar asked Aug 30 '16 20:08

bosticko


1 Answers

All you need to do is move your CTE inside the string as the first argument to crosstab(text, text) function just like you did with the select statement. It will be parsed and executed properly. This is because you provide full SQL statement that produces the source set in first argument.

You need to double your quotation marks inside the string or use dollar-quoting $$ just like you did with the second argument and I did below:

SELECT * FROM
  CROSSTAB(
    $$
    WITH common_table(name, category, category_value) AS (
      VALUES
        ('A',  'foo',    1             ),
        ('A',  'bar',    2             ),
        ('B',  'foo',    3             ),
        ('B',  'bar',    4             )
    )
    SELECT * FROM common_table $$,
    $$
      VALUES
        ('foo'),
        ('bar')
    $$
  ) AS (
    name TEXT,
    foo  INT,
    bar  INT
  );

Result

 name | foo | bar
------+-----+-----
 A    |   1 |   2
 B    |   3 |   4
like image 166
Kamil Gosciminski Avatar answered Oct 31 '22 20:10

Kamil Gosciminski