1

It's all in the title. Documentation has something like this:

SELECT * 
FROM crosstab('...') AS ct(row_name text, category_1 text, category_2 text);

I have two tables, lab_tests and lab_tests_results. All of the lab_tests_results rows are tied to the primary key id integer in the lab_tests table. I'm trying to make a pivot table where the lab tests (identified by an integer) are row headers and the respective results are in the table. I can't get around a syntax error at or around the integer.

Is this possible with the current set up? Am I missing something in the documentation? Or do I need to perform an inner join of sorts to make the categories strings? Or modify the lab_tests_results table to use a text identifier for the lab tests?

Thanks for the help, all. Much appreciated.

Edit: Got it figured out with the help of Dmitry. He had the data layout figured out, but I was unclear on what kind of output I needed. I was trying to get the pivot table to be based on batch_id numbers in the lab_tests_results table. Had to hammer out the base query and casting data types.

SELECT *
 FROM crosstab('SELECT lab_tests_results.batch_id, lab_tests.test_name, lab_tests_results.test_result::FLOAT
                FROM lab_tests_results, lab_tests
                WHERE lab_tests.id=lab_tests_results.lab_test AND (lab_tests.test_name LIKE ''Test Name 1'' OR lab_tests.test_name LIKE ''Test Name 2'')
                ORDER BY 1,2'
            )   AS final_result(batch_id VARCHAR, test_name_1 FLOAT, test_name_2 FLOAT);

This provides a pivot table from the lab_tests_results table like below:

batch_id   |test_name_1 |test_name_2 
---------------------------------------
batch1     |  result1   |   <null>
batch2     |  result2   |  result3  
user1644030
  • 251
  • 3
  • 10

1 Answers1

1

If I understand correctly your tables look something like this:

CREATE TABLE lab_tests (
  id INTEGER PRIMARY KEY,
  name VARCHAR(500)
);

CREATE TABLE lab_tests_results (
  id INTEGER PRIMARY KEY,
  lab_tests_id INTEGER REFERENCES lab_tests (id),
  result TEXT
);

And your data looks something like this:

INSERT INTO lab_tests (id, name) 
VALUES (1, 'test1'),
       (2, 'test2');

INSERT INTO lab_tests_results (id, lab_tests_id, result)
VALUES (1,1,'result1'),
       (2,1,'result2'),
       (3,2,'result3'),
       (4,2,'result4'),
       (5,2,'result5');

First of all crosstab is part of tablefunc, you need to enable it:

CREATE EXTENSION tablefunc;

You need to run it one per database as per this answer.

The final query will look like this:

SELECT *
FROM crosstab(
    'SELECT lt.name::TEXT, lt.id, ltr.result
     FROM lab_tests AS lt
     JOIN lab_tests_results ltr ON ltr.lab_tests_id = lt.id'
) AS ct(test_name text, result_1 text, result_2 text, result_3 text);

Explanation:
The crosstab() function takes a text of a query which should return 3 columns; (1) a column for name of a group, (2) a column for grouping, (3) the value. The wrapping query just selects all the values those crosstab() returns and defines the list of columns after (the part after AS). First is the category name (test_name) and then the values (result_1, result_2). In my query I'll get up to 3 results. If I have more then 3 results then I won't see them, If I have less then 3 results I'll get nulls.

The result for this query is:

test_name |result_1 |result_2 |result_3
---------------------------------------
test1     |result1  |result2  |<null>
test2     |result3  |result4  |result5
Dmitry
  • 6,716
  • 14
  • 37
  • 39
  • Thanks Dmitry. Got the output I wanted figured out from what you provided. Edited my original post to show that. – user1644030 Dec 19 '17 at 20:53