0

I have a data-set consisting of Student and Subject in a PostgreSQL Database. The relations look something like:

Student:
id,
name,
...

Student ID  | Name      | ... 
1 | Ramesh | ...
2 | Suresh | ...

Subject:
id,
name,
...

Subject ID  | Name      | ...
1 | Maths | ...
2 | Science | ...

Student_Subject:
id,
student_id,
subject_id,
...

The third table, as one could imagine, it's a form of representing many-to-many mapping. Suppose a student has selected 3 subjects, he will have 3 mappings against his ID in the student_subject table.

There is no restriction on the number of subjects that a student may select it could be anything between 0 and 10.

I need to create a single SQL query, that will fetch records in this format:

Student ID  | Student Name  | Maths         | Science       | ...   | History
1           | Ramesh        | Y             | N             | ...   | Y
2           | Suresh        | N             | Y             | ...   | Y

The Subject names could be hardcoded as column aliases that's fine.

Can someone pls suggest how this can be achieved?

I tried using the case when technique as:

    select 
        stud.name,
        (case when sub.name = 'Maths' then 'Y' else 'N' end) "Maths",
        (case when sub.name = 'Science' then 'Y' else 'N' end) "Science", 
        ...
    from student stud
        inner join student_subject s_s on s_s.student_id = stud.id
        inner join subject sub on sub.id = s_s.student_id
    ;

But this way I'm not getting one row per student. If the student has selected 3 subjects, I'm getting 3 different rows with one Y value against each of the subjects on each row.

1 Answer 1

1

Static solution based on a fix list of subjects :

You have to group the resulting rows by student so that to get one row per student, and for each group, use the bool_or() aggregate function which returns True when at least on row in the group returns True :

select 
    stud.name,
    bool_or(case when sub.name = 'Maths' then True else False end) "Maths",
    bool_or(case when sub.name = 'Science' then True else False end) "Science", 
    ...
from student stud
    inner join student_subject s_s on s_s.student_id = stud.id
    inner join subject sub on sub.id = s_s.student_id
group by stud.name

Dynamic solution when the list of subjects may evolve :

see How to dynamically add columns in PostgresSQL select?

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

Comments

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.