Need help in complex postgresql select - sql

I have 3 tables in my database.
One is for activities, another one is for students and the last one is for the student's score in an activity. So one activity can have one or more student's scores, and one student has one score in one activity.
So I need to show a list of activities of student's course even if he has not score yet. But, if the student has a score in some activity, I must only show that student's score.
My problem is that when I select the student's score, I need to add a 'where' condition to select only his scores, but if he has not score yet, I have no records in my results. How can I select all activities of a course and only if the student has score, then select only his score for that activity?
Activity Table:
id | name | score
1 | first exam | 30
2 | second exam | 30
3 | final exam | 40
Student Table:
id | name
1 | Jhon
2 | Sam
3 | Frank
Student's Score Table:
id | activity | student | score
1 | 1 | 1 | 16
2 | 1 | 2 | 17
3 | 1 | 3 | 15
So, if Jhon enter to the system and want to see activities's course, he should see a list of all activities but only his score. For example this list:
Activity | Score | Your Score
First Exam | 30 | 16
Second Exam | 30 | -
Final Exam | 40 | -

You want a left outer join:
select a.activity_id, coalesce(acs.score, 0) as thescore
from activities a left outer join
activityscores acs
on a.activity_id = acs.activity_id and
acs.student_id = THESTUDENTID;
If a student can have more than one score in an activity, then you would want an aggregation as well.

Related

Access 2016 - Return TRUE if Item exists in Table, False if Item is Missing

Ok, I have two tables.
1 lists a bunch of courses and their details.
1 lists a bunch of curriculums and their associated connections.
I need to create a query that will list the curriculum name, LIST ALL COURSES, and then indicate if the course is in the Curriculum (TRUE) or not present (FALSE).
Here is what I have tried so far.
Left Outer Join for courses table since I want ALL the courses compared to the curriculums
Count number of curriculum ids for each course - DOESN'T WORK ONLY RETURNS '1' VALUES; AND ONLY SHOWS COURSES IN THE CURRICULUM
Finding Differences Between Two Tables - DOESN'T WORK, ONLY SHOWS VALUES WITH NO CURRICULUMS.
Tried an IIF(IS NULL) calculation - DOESN'T WORK ONLY RETURNS 'TRUE' VALUES; AND ONLY SHOWS COURSES IN THE CURRICULUM.
This should be easy. Can someone help me create the query. Basically put I need all values from BOTH tables showing up and then showing where the value is NULL in relation to the courses.
Table 1:
COURSE ID COURSE NAME
1 ENGLISH
2 FRENCH
3 DRAWING
4 SKETCHING
Table 2
Curriculum ID Curriculum NameID Course ID
1 Senior (actually #) 1
2 Senior 3
3 Junior 1
4 Junior 2
5 Junior 3
Results
Curriculum Name Course Name In Curriculum
Senior English True
Senior French False
Senior Drawing True
Senior Sketching False
Junior English True
Junior French True
Junior Drawing True
Junior Sketching False
TJ
Since you essentially have a many-to-many relationship between courses & curriculums (a course could appear on several curriculums, and a curriculum could contain several courses), I would suggest structuring your data in the following manner:
Table: Courses
+-------+-----------+
| Co_ID | Co_Desc |
+-------+-----------+
| 1 | English |
| 2 | French |
| 3 | Drawing |
| 4 | Sketching |
+-------+-----------+
Table: Curriculums
+-------+---------+
| Cu_ID | Cu_Desc |
+-------+---------+
| 1 | Junior |
| 2 | Senior |
+-------+---------+
Junction Table: Curriculum_Courses
+----------+----------+
| CC_Cu_ID | CC_Co_ID |
+----------+----------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 3 |
+----------+----------+
Then, your query is relatively easy to construct as you can use a combination of a cross-join and left-join in the following manner:
select
t.cu_desc as [Curriculum Name],
t.co_desc as [Course Name],
not cc_cu_id is null as [In Curriculum]
from
(select * from curriculums, courses) t left join curriculum_courses u on
t.cu_id = u.cc_cu_id and
t.co_id = u.cc_co_id
order by
t.cu_id, t.co_id

How to check if two columns are consistent in a table. sql

I'm struggling to ask the question so I will just put an example table. Basically, if I have two columns with headings person and insured car, how can I check if the same person consistently insures the same brand of car.
------|------
|person|brand |
------|------
| 0 |Toyota|
| 0 |Mazda |
| 1 |Toyota|
| 1 |Toyota|
| 2 |Honda |
| 2 |Honda |
| 3 |Ford |
------|------
So basically in this table I want to filter out person 0 because he insures both Toyota's and Mazda's, however the other people exclusively insure one brand of a car.
Thanks.
If you just want the people and car, you can use aggregation:
select person, min(brand) as the_brand
from t
group by person
having min(brand) = max(brand);

How do I use group by with a value that should always be included?

I asked a similar question; however, I asked it incorrectly before. Let's say I have the following table:
+-----------+------------+-------+
| quiz_type | student_id | score |
+-----------+------------+-------+
| class | NULL | 10 |
+-----------+------------+-------+
| class | NULL | 9 |
+-----------+------------+-------+
| student | A | 5 |
+-----------+------------+-------+
| student | B | 7 |
+-----------+------------+-------+
| student | A | 6 |
+-----------+------------+-------+
I want to get the standard deviation of the scores for each student, but need to include the class scores for every student. In reality, the quiz_type column doesn't exist (it's just to better show the example). I need to do a GROUP BY student_id, but include the NULL values with every group. I've been struggling with this for quite a bit. Is there a good solution?
For the sake of example, I'd like to use the aggregate AVG function to get a table like the following:
+------------+---------+
| student_id | Average |
+------------+---------+
| A | 7.5 |
+------------+---------+
| B | 8.67 |
+------------+---------+
In reality I will be calling the STDDEV_SAMP function.
One clever way to do this is to self join your table in such a way that the NULL values get paired up with every non NULL entry. Then, you can use both score columns in your calculation. Try something like this:
SELECT t2.student_id,
SUM(t2.score) / (SELECT SUM(CASE WHEN student_id IS NULL THEN 1 ELSE 0 END) FROM students) AS nonNullScore,
(SUM(t1.score) / COUNT(*)) * (SELECT SUM(CASE WHEN student_id IS NULL THEN 1 ELSE 0 END) FROM students) AS nullScore
FROM students t1
INNER JOIN students t2
ON t1.student_id IS NULL AND t2.student_id IS NOT NULL
GROUP BY t2.student_id
I tested this query in MySQL Workbench and it appears to be working.
Output:
student_id | nonNullScore | nullScore
A | 11.0000 | 19.0000
B | 7.0000 | 19.0000
From the question, the mean value of score can be adjusted by adding the scores for null values to the total. The adjusted standard deviation can then be calculated from the adjusted mean per student.
SELECT
student_id,
SQRT(AVG(squared_diff)) adjusted_std_deviation
FROM (SELECT
t.student_id,
pow((t.score - x.adjmean), 2) squared_diff
FROM t
CROSS JOIN (SELECT avg(1.0*score) adjmean FROM t) x
WHERE student_id IS NOT NULL) y
GROUP BY student_id
ORDER BY 1
Sample Fiddle
Calculating Standard Deviation

How to establish join and self join on the same row

The situation is like this:
There is Teacher Performance and Student Performance entity, so teacher and student may attend same course have their score
Teacher can teach student and teacher
There is a Teaching Relation table, and yes both teacher and student can appear in attendance column, but only teacher can appear in teacher column.
The desired report table is list all teacher's performance, plus average performance of the students and teachers who are taught by the teacher.
In our system the id is numeric, I made it first name just for demo purpose.
Teacher Performance(tb_tp)
t_id | score
------------------
JOHN | 5
ASHLEY | 6
STEVEN |4.5
Student Performance(tb_sp)
s_id | score
------------------
SCOTT | 5
FRANK | 8
TIM | 7
Teaching Relation(tb_tr)
t_id (teacher) | a_id for attendance id | a_type attendance type
------------------------------------------------------------------
ASHLEY | JOHN | teacher
ASHLEY | FRANK | student
ASHLEY | TIM | student
JOHN | ASHLEY | teacher
JOHN | FRANK | student
Desired Report:
t_id | Score| avg_t AVG score from Teacher | avg_s AVG score from Student
--------------------------------------------------------------------------
ASHLEY | 6 | (5 from John) /1 | (8 from Frank + 7 from Tim)/2
--------------------------------------------------------------------------
JOHN | 5 | (6 from ASHLEY) /1 | (8 from Frank) / 1
How can we make this happen? I am thinking two joins together or union two queries, but either way cannot keep the result on the same row and be accurate
I think the technique you want is two outer joins with an aggregate. Here is my untested pass at it:
SELECT p.t_id, p.score, scores.avg_teacher_score, scores.avg_student_score
FROM teacher_performance p
JOIN (
SELECT t_id, AVG( t.score) AS avg_teacher_score, AVG( s.score ) AS avg_student_score
FROM teaching_relation tr
LEFT JOIN teacher_performance t ON t.t_id = tr.a_id AND tr.a_type='teacher'
LEFT JOIN student_performance s ON s.s_id = tr.a_id AND tr.a_type='student'
) scores
ON scores.t_id = p.t_id

SQL display status for records with null values

I am trying to create a query that lists records from table 1 along with a status based on corresponding records in table 2 that have null values in one or more of the fields. The problem I am running into is how to include records from table 1 which have no corresponding record in table 2.
In my example, I want to list the names of all students in tblStudent along with a field indicating the status of their schedule in tblStudentSchedule. If either course or teacher field in tblStudentSchedule is Null or no corresponding record in tblStudentSchedule is found then I want to display "Incomplete". Otherwise, I want to display "Complete".
desired result
Name | Schedule Status
-----------------------
Bob | Incomplete
Sally | Incomplete
Jane | Incomplete
Matt | Incomplete
Jim | Complete
I'm working in Access. I would post my query attempts but I think they would just confuse the issue. This is probably very basic but I am having a mental block trying to wrap my brain around this one.
tblStudent
studentID | studentName
-----------------------
1 | Bob
2 | Sally
3 | Jane
4 | Matt
5 | Jim
tblStudentSchedule
studentID | period | course | teacher
-------------------------------------
1 | 1 | math | Jones
1 | 2 | <null> | Watson
2 | 1 | reading| <null>
4 | 1 | <null> | Crick
5 | 1 | math | Jones
select s.studentName as Name
, iif(sum(iif(ss.course is null or ss.teacher is null, 1, 0)) = 0,
'Complete', 'Incomplete')
as [Schedule Status]
from tblStudent s
left join
tblStudentSchedule ss
on ss.studentID = s.studentID
group by
s.studentName
A left join returns a single row with null when a match is not found. So the check on ss.course is null will also trigger when the student is absent from the schedule table.
If no corresponding record in tblStudentSchedule is found, you can get rows from this table as null coulmns by using left or right joins. Read below:
http://office.microsoft.com/en-us/access-help/left-join-right-join-operations-HP001032251.aspx
And then to convert null column use isnull function
http://www.techonthenet.com/access/functions/advanced/isnull.php
Or use a case statement to check for null
http://www.techonthenet.com/access/functions/advanced/case.php