0

What i have

Query:

SELECT u.firstname,u.lastname, u.institution as department, u.department as company, c.shortname,  
to_char(to_timestamp(p.timecompleted)::date,'YYYY-MM-DD') AS completed
FROM mdl_course_completions AS p
JOIN mdl_course AS c ON p.course = c.id
JOIN mdl_user AS u ON p.userid = u.id
WHERE c.enablecompletion = 1 AND u.firstname is NOT NULL AND p.timecompleted is NOT NULL
ORDER BY u.firstname

Results:

firstname   lastname   department   company   course    completed
u1          u1         x            x          c1        date
u1          u1         x            x          c2        date

What i need

I need to be able to transport course to columns. Resulting in something similar to this:

firstname   lastname   department   company   c1    c2
u1          u1         x            x         date  date
u2          u2         x            x         date  date   

I have tried using crosstab, but I am not skilled enough on SQL. Could someone please help?

EDIT: the number of courses are in the hundreds, so it needs to be dynamic.

(Also: English is not my first language, so please excuse any unclarities).

1
  • 1
    Crosstab questions Commented Mar 23, 2021 at 6:53

2 Answers 2

1

This is not what you request, but maybe be useful for you. Last column is an array with the structure: {'course_name: course_completed', ...}.

SELECT u.firstname,u.lastname, u.institution as department, u.department as company
    array_agg(c.shortname || ': ' || to_char(to_timestamp(p.timecompleted)::date,'YYYY-MM-DD')) AS courses_completed
FROM mdl_course_completions AS p
JOIN mdl_course AS c ON p.course = c.id
JOIN mdl_user AS u ON p.userid = u.id
WHERE c.enablecompletion = 1 AND u.firstname is NOT NULL AND p.timecompleted is NOT NULL
GROUP BY u.firstname,u.lastname, u.institution, u.department
ORDER BY u.firstname

output will be:

firstname   lastname   department   company   courses_completed
u1          u1         x            x         {"c1: date", "c2: date"}
u2          u2         x            x         {"c1: date", "c2: date"}

Other option is to use crosstab function (like you said): Postgresql crosstab documentation. You have to install tablefunc module to use crosstab. Read the documentation and if you think it could be useful for you I could help you to write the query (but I will need to know the tables structure and some example data).

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! This is very usefull and should do for now :)
0

You can use conditional aggregate, for example by using the FILTER clause:

SELECT
    firstname, lastname, department, company,
    MAX(completed) FILTER (WHERE course = 'c1') as c1,
    MAX(completed) FILTER (WHERE course = 'c2') as c2
FROM 
    -- <joins>
GROUP BY firstname, lastname, department, company

You need to group by the columns which are part of the same group. Then you can execute any aggregation function you like on the columns you want to pivot. With the appended FILTER clause you can filter all records which should be recognized for the aggregate.

3 Comments

Thank you for your answer. The number of courses are however in the hundreds, so this would be a very lengthy SQL.
@user2075124: there is no other way - if you need hundreds of columns you will have to write hundreds of expressions (one for each column)
Yes, it would be. However, using crosstab, you have to explicitly enumerate the columns as well... There's no way to create columns dynamically.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.