2

I have a table that looks like this:

Project Date System Result
Proj1 07-01 A PASS
Proj1 07-01 B PASS
Proj1 07-01 C PASS
Proj1 07-01 D PASS
Proj1 07-02 A FAIL
Proj1 07-02 B FAIL
Proj1 07-02 C FAIL
Proj1 07-02 D FAIL
Proj2 07-01 E PASS
Proj2 07-01 F FAIL
Proj2 07-02 E PASS
Proj2 07-02 F PASS

I want it to end up like this:

Project Date A B C D E F
Proj1 07-01 PASS PASS PASS PASS
Proj1 07-02 FAIL FAIL FAIL FAIL
Proj2 07-01 PASS PASS FAIL
Proj2 07-02 PASS PASS PASS

I was also wondering if it was possible to produce something like this where the order of the various systems are just alphabetical.

Project Date System1 System2 System3 System4
Proj1 07-01 A-PASS B-PASS C-PASS D-PASS
Proj1 07-02 A-FAIL B-FAIL C-FAIL D-FAIL
Proj2 07-01 E-PASS F-FAIL
Proj2 07-02 E-PASS F-PASS

I have been trying for the last few days with the crosstab function and I am not able to reproduce the results I want. Any help would be much appreciated - thank you so much!

  • How does "System1", "System2" etc align with each row - why is `D-PASS` not under "System1"? Note - there is no ordering to a set of rows unless you can specify it. – Stu Jul 28 '22 at 20:43
  • @Stu I was planning it on being alphabetical – wadeland1738 Jul 29 '22 at 00:53

1 Answers1

0

Try the following without using Crosstab:

Select Project,Date_,
Max(Case When System_='A' Then Result_ Else '' End) As A,
Max(Case When System_='B' Then Result_ Else '' End) As B,
Max(Case When System_='C' Then Result_ Else '' End) As C,
Max(Case When System_='D' Then Result_ Else '' End) As D,
Max(Case When System_='E' Then Result_ Else '' End) As E,
Max(Case When System_='F' Then Result_ Else '' End) As F
From your_table
Group By Project,Date_
Order By Project,Date_

See a demo from db-fiddle.

If the System column has an undetermined number of values (as you commented), then you have to use Dynamic SQL. I'm not proffenceinal in that, but the following will pay the bill:

First, create a function to prepare the dynamic statement as the following:

Create Or Replace Function pvt()
    RETURNS void
    LANGUAGE 'plpgsql'
As $body$
Declare
    sqlColumn varchar;
    qr varchar;
    columnlist varchar;
Begin
    sqlColumn= 'select distinct system_ from your_table order by system_;';
    qr='prepare pvtstmt as Select Project,Date_,';
    
    For columnlist In EXECUTE sqlColumn  
      Loop
          qr = qr || 'Max(Case When System_='|| chr(39) || columnlist ||
          chr(39) ||' Then Result_ Else ' ||chr(39)||chr(39)||' End) As 
          ' ||  columnlist || ', ';
      End Loop;

    qr = substr(qr, 0, length(qr) - 1);
    qr = qr || 'From your_table Group By Project,Date_ Order By Project,Date_';
   
    Deallocate All;
    EXECUTE qr;
End;
$body$;

Now, call that function to create the prepared statement and execute that statement as the following:

Select pvt();
Execute pvtstmt;

Where pvtstmt is the name of the prepared statement in the function.

See a demo from db-fiddle.

Note: in the demo, the label $body$ is replaced with a single quote, just to run the function on the fiddle.

For the second version of the output format, (System1, System2,...) instead of system name, you may use a view that return Row_Number() Over (Partition By Project,Date_ Order By System_). So the pvt() function will be as the following:

Create Or Replace Function pvt()
    RETURNS void
    LANGUAGE 'plpgsql'
As $body$
Declare
    sqlColumn varchar;
    qr varchar;
    columnlist varchar;
Begin
    sqlColumn= 'select distinct D.sys_cat from (select Row_Number() Over (Partition By Project,Date_ Order By System_) as sys_cat From your_table) D order by D.sys_cat;';
    qr='prepare pvtstmt as Select D.Project,D.Date_,';
    
    For columnlist In EXECUTE sqlColumn  
      Loop
          qr=qr || ' Max(Case When sys_cat='|| chr(39) || columnlist || chr(39) ||' Then System_ ||' ||chr(39)||'-'||chr(39)||'|| Result_ Else ' ||chr(39)||chr(39)||' End) As System' ||  columnlist ||  ' , ';
      End Loop;

    qr = substr(qr, 0, length(qr) - 1);
    qr=qr || 'From (select *, Row_Number() Over (Partition By Project,Date_ Order By System_) as sys_cat From your_table) D Group By D.Project,D.Date_ Order By D.Project,D.Date_;';
   
    Deallocate All;
    EXECUTE qr;
End;
$body$;

Check this demo.

ahmed
  • 9,071
  • 3
  • 9
  • 22
  • Hey - this works perfectly! The only question I have is my actual database table is very large and it seems inefficient to list out each system as a new Case. Do you happen to know if there is an easier way? – wadeland1738 Jul 29 '22 at 00:43
  • @wadeland1738, in this case you have to use dynamic sql query, I will try to update the answer according to that. – ahmed Jul 29 '22 at 02:49
  • awesome, I had never thought about using a dynamic query. If I wanted to include a filter (for ex. WHERE Project = 'Proj1'), where would I include that within the query? – wadeland1738 Jul 29 '22 at 18:15
  • Before the `Group by` of the outer query, `From ... Where D.Project='Proj1' Group By D.Project, D.Date_ Order By D.Project, D.Date_` – ahmed Jul 29 '22 at 19:23
  • I tried that - it doesn't seem to work (not sure if I am missing something) – wadeland1738 Jul 29 '22 at 19:34
  • Check this, https://www.db-fiddle.com/f/vUYSGQfqxSkhmd7tYGMXGs/4 – ahmed Jul 29 '22 at 19:37
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/246895/discussion-between-wadeland1738-and-ahmed). – wadeland1738 Jul 29 '22 at 19:42
  • Thank you so much! Also, I just had one last question. Do you have experience with using Node/Express to set up an API with a dynamic query such as the one you have wrote? I am not sure how to reference the function correctly. – wadeland1738 Jul 29 '22 at 19:44
  • Unfortunately, no, but I guess that all what you need is `Select pvt(); Execute pvtstmt;`, and you may use it as regular query. – ahmed Jul 29 '22 at 19:50
  • Ok will try that out. Also, my WHERE clause is a little more complicated: WHERE D.Project IN (SELECT D.Project FROM mapping WHERE Scenario = "Scenario1") Do you know what the proper syntax would be (mapping is another table in my DB and scenario is a column in the mapping table) – wadeland1738 Jul 29 '22 at 19:51
  • "WHERE clause is a little more complicated", you may test it with non dynamic query before using it with the dynamic one, once it's Ok copy it to the dynamic one. Good Luck. – ahmed Jul 29 '22 at 19:55
  • do you know if there is a way to store the result of the Execute statement in a table? – wadeland1738 Aug 01 '22 at 17:35
  • Dear, I can't answer all of your questions in the comments, post another question to get better answers. – ahmed Aug 02 '22 at 00:20