Too many results from query - sql

I'm trying to both understand the following query,
SELECT s.LAST_NAME||', '||s.FIRST_NAME||' '||COALESCE(s.MIDDLE_NAME,' ') AS FULL_NAME,
s.LAST_NAME,
s.FIRST_NAME,
s.MIDDLE_NAME,
s.STUDENT_ID,
ssm.SCHOOL_ID,
ssm.SCHOOL_ID AS LIST_SCHOOL_ID,
ssm.GRADE_ID ,
sg1.BENCHMARK_ID,
sg1.GRADE_TITLE,
sg1.COMMENT AS COMMENT_TITLE,
ssm.STUDENT_ID,
sg1.MARKING_PERIOD_ID,
sg1.LONGER_COURSE_COMMENTS,
sp.SORT_ORDER,
sched.COURSE_PERIOD_ID
FROM STUDENTS s,
STUDENT_ENROLLMENT ssm ,
SCHEDULE sched
LEFT OUTER JOIN STUDENT_REPORT_CARD_BENCHMARKS sg1 ON (
sg1.STUDENT_ID=sched.STUDENT_ID
AND sched.COURSE_PERIOD_ID=sg1.COURSE_PERIOD_ID
AND sg1.MARKING_PERIOD_ID IN ('0','442','445','450')
AND sg1.SYEAR=sched.SYEAR)
LEFT OUTER JOIN COURSE_PERIODS rc_cp ON (
rc_cp.COURSE_PERIOD_ID=sg1.COURSE_PERIOD_ID
AND rc_cp.DOES_GRADES='Y')
LEFT OUTER JOIN SCHOOL_PERIODS sp ON (sp.PERIOD_ID=rc_cp.PERIOD_ID)
WHERE ssm.STUDENT_ID=s.STUDENT_ID
AND ssm.SCHOOL_ID='1'
AND ssm.SYEAR='2010'
AND ('22-APR-11' BETWEEN ssm.START_DATE AND ssm.END_DATE OR (ssm.END_DATE IS NULL))
AND (LOWER(s.LAST_NAME) LIKE 'la''porsha%' OR LOWER(s.FIRST_NAME) LIKE 'la''porsha%' )
AND sched.STUDENT_ID=ssm.STUDENT_ID AND sched.MARKING_PERIOD_ID IN ('0','444','446','447','445','448','450','443','449')
AND ('22-APR-11' BETWEEN sched.START_DATE AND sched.END_DATE OR (sched.END_DATE IS NULL AND '22-APR-11'>=sched.START_DATE))
ORDER BY s.LAST_NAME,s.FIRST_NAME
and modify it to return the correct results - to only return one distinct person. When any particular person is searched for, multiple results are returned because there are unique values returned from schedule.course_period_id. As there are several left outer joins on the course_period_id field but across different tables, I'm confused as to where to modify the query.

My attempt to help people answer by formatting your query and getting rid of the mixed syntax. Not really an answer but too long for a comment:
SELECT s.LAST_NAME || ', ' || s.FIRST_NAME || ' ' || COALESCE(s.MIDDLE_NAME,' ')
AS FULL_NAME,
s.LAST_NAME, s.FIRST_NAME, s.MIDDLE_NAME, s.STUDENT_ID,
ssm.SCHOOL_ID, ssm.SCHOOL_ID AS LIST_SCHOOL_ID, ssm.GRADE_ID ,
sg1.BENCHMARK_ID, sg1.GRADE_TITLE, sg1.COMMENT AS COMMENT_TITLE,
ssm.STUDENT_ID, sg1.MARKING_PERIOD_ID, sg1.LONGER_COURSE_COMMENTS,
sp.SORT_ORDER, sched.COURSE_PERIOD_ID
FROM STUDENTS s
INNER JOIN STUDENT_ENROLLMENT ssm
ON ssm.STUDENT_ID=s.STUDENT_ID -- moved from WHERE to here
INNER JOIN SCHEDULE sched
ON sched.STUDENT_ID=ssm.STUDENT_ID -- moved from WHERE to here
LEFT OUTER JOIN STUDENT_REPORT_CARD_BENCHMARKS sg1
ON ( sg1.STUDENT_ID=sched.STUDENT_ID
AND sched.COURSE_PERIOD_ID=sg1.COURSE_PERIOD_ID
AND sg1.MARKING_PERIOD_ID IN ('0','442','445','450')
AND sg1.SYEAR=sched.SYEAR)
LEFT OUTER JOIN COURSE_PERIODS rc_cp
ON ( rc_cp.COURSE_PERIOD_ID=sg1.COURSE_PERIOD_ID
AND rc_cp.DOES_GRADES='Y')
LEFT OUTER JOIN SCHOOL_PERIODS sp
ON (sp.PERIOD_ID=rc_cp.PERIOD_ID)
WHERE ssm.SCHOOL_ID='1'
AND ssm.SYEAR='2010'
AND ('22-APR-11' BETWEEN ssm.START_DATE AND ssm.END_DATE
OR (ssm.END_DATE IS NULL))
AND ( LOWER(s.LAST_NAME) LIKE 'la''porsha%'
OR LOWER(s.FIRST_NAME) LIKE 'la''porsha%' )
AND sched.MARKING_PERIOD_ID
IN ('0','444','446','447','445','448','450','443','449')
AND ( '22-APR-11' BETWEEN sched.START_DATE AND sched.END_DATE
OR ( sched.END_DATE IS NULL
AND '22-APR-11' >= sched.START_DATE))
ORDER BY s.LAST_NAME, s.FIRST_NAME
Hope it helps.

Well of course you have mulitple records if the child tables joined to have multiple records for the same person. That is expected and correct behavior.
If you only want one record per person, then you must modify the query to tell it which of the multiple child records you want it to choose. But why wouldn't you want to see all the scheduled courses for the person, instead of only one?
If you must you coudl use group by and then put an aggregate (like min or max) on the fields which are causing you the multiple records. However, you would still need to know if you only want the first period records or the last period records or how would you decide out of six records for the person which one you want to see?

Look up the group by clause.

Related

Select A column from Subquery

I have a query like below
Select Student_ID, Name, School
From Student S
Where S.Student_ID in ( select associate_id from Details D)
The output count is coming as expected
But now I have a new requirement to get additional column (D.Subject)in the data from the Details table
Select S.Student_ID, S.Name, S.School, D.Subject
From Student S
Where S.Student_ID in ( select associate_id from Details D)
When I’m trying to achieve the above by using the join like below the count is not matching . I tried both Left outer and inner join and the count doesn’t come out correctly .
Select S.Student_ID, S.Name, S.School, D.Subject
From Student S
Left outer join Details D on S.Student_ID = D.associate_id
Where S.Student_ID in ( select associate_id from Details D)
Please let me know how to achieve this
Since we dont have the sample data, I tried to put something together based on your question
problem is with the distinct count.
You should use distinct values in select. Dont really need the where condition, as that will be covered in your join condition.
Select distinct S.Student_ID, S.Name, S.School, D.Subject
From Student S join Details D on S.Student_ID = D.associate_id;
this will give you the unique counts there are. As there may be more join conditions between these tables and not just the column student_id to associate_id
for ex-
If i take the example below
and
in line query will give you a different count then inner join.
So either you find a correct join conditions or distinct the values to keep you close.

Filtering SELECT TOP WITH TIES When No Records Exist for a Specific Column

Question: How can I filter my results (see below) to exclude erroneous data? I'm guessing my problem is somewhere in the WHERE clause but for the life of me, I can't figure it out.
End Goal: Return NULL values for the CDA_Orientation column where no values exist in the portfolio and e_component tables (e.g. employee has not had Orientation yet)
DB Schema:
Result Set with Errors:
NOTE: The Orientation dates for Eastman, DeLuca, and Fontano are the same date and represent the TOP 1 result from the course_startdate column of the portfolio table.
What I Want the Results to Look Like:
If I've done my JOINS correctly, the CDA_Orientation column should show NULL because there is no entry in the portfolio table (and accordingly, the e_component table) for these three individuals. The entry is only created by the front end when the Employee is assigned to a course.
Here is My Code:
SELECT TOP (1) WITH TIES
P.lastname+', '+P.firstname AS Employee,
P.person_id,
CONVERT(DATE,PC.CDAI_EXP_DATE) AS CDA_Infant,
CONVERT(DATE,PC.CDAP_EXP_DATE) AS CDA_Preschool,
CONVERT(DATE,PO.course_startdate) AS CDA_Orientation
FROM person P
JOIN person_custom PC ON PC.person_id=P.person_id
LEFT JOIN portfolio PO ON P.person_id=PO.person_id
FULL JOIN e_component EC ON PO.component_id=EC.component_id
WHERE (cdai_exp_date IS NOT NULL OR cdap_exp_date IS NOT NULL)
AND PO.course_startdate IN (SELECT course_startdate
FROM portfolio PO
LEFT JOIN e_component EC ON PO.component_id=EC.component_id
WHERE (EC.userdefined_id LIKE '000150%' AND PO.status=11))
ORDER BY ROW_NUMBER() OVER(PARTITION BY P.lastname+', '+P.firstname
ORDER BY PO.person_id)
NOTE: The TOP (1) WITH TIES has successfully pulled the most recent orientation date (employees can have more than one) from the portfolio table for Tarkin and Rust. I've cut out any and all unnecessary JOINS and caveats.
Thanks in advance!
I believe the joins are the issue. Using WITH TIES in that way is also confusing if you're just trying to get a record for each person; I would use a GROUP BY. If you wanted to do it without a sub-query you could do:
SELECT
P.lastname+', '+P.firstname AS Employee,
P.person_id,
CONVERT(DATE,PC.CDAI_EXP_DATE) AS CDA_Infant,
CONVERT(DATE,PC.CDAP_EXP_DATE) AS CDA_Preschool,
MAX(CONVERT(DATE,PO.course_startdate)) AS CDA_Orientation
FROM #person P
JOIN #person_custom PC
ON PC.person_id=P.person_id
LEFT JOIN
(#portfolio PO
JOIN #e_component EC
ON PO.component_id=EC.component_id
AND EC.userdefined_id LIKE '000150%'
AND PO.status=11)
ON P.person_id=PO.person_id
WHERE (cdai_exp_date IS NOT NULL OR cdap_exp_date IS NOT NULL)
GROUP BY P.lastname, P.firstname, P.person_id,PC.CDAI_EXP_DATE,PC.CDAP_EXP_DATE

What Join to use against 2 Tables for All Data

Hi I am looking to find out what join I would use if I wanted to join 2 tables together. I currently have a list of all students so 25 students to 1 class and the other table only shows 7 of those names with their test results.
What I would like is to have 1:1 join for the ones with the test results and the other ones without I would like to show them underneath so all in all I have 20 records.
If somebody could please advise on how I could achieve this please.
Thanks in advance.
It sounds like you want an OUTER JOIN.
For this example, we'll assume that there is a table named student and that it contains a column named id which is UNIQUE (or PRIMARY) KEY.
We'll also assume that there is another table named test_result which contains a column named student_id, and that column is a foreign key referencing the id column in student.
For demonstration purposes, we'll just make up some names for the other columns that might appear in these tables, name and score.
SELECT s.id
, s.name
, r.score
FROM student s
LEFT
JOIN test_result r
ON r.student_id = s.id
ORDER
BY r.student_id IS NULL
, s.score DESC
, s.id
Note that if student_id is not unique in test_result, there is potential to return multiple rows that match a row in student.
To get (at most) one row returned from test_result per student, we could use an inline view.
SELECT s.id
, s.name
, r.score
FROM student s
LEFT
JOIN ( SELECT t.student_id
, MAX(t.score) AS score
FROM test_result t
GROUP BY t.student_id
) r
ON r.student_id = s.id
ORDER
BY r.student_id IS NULL
, s.score DESC
, s.id
The expressions in the ORDER BY clause are designed to return the students that have matching row(s) in test_result first, followed by students that don't.
This is just a demonstration, and very likely excludes some important criteria, such as which test a score should be returned for. But without a sample schema and some example data, we're just guessing.
You are looking for a left outer join or a full outer join.
The left outer join will show all students and their tests if they have them.
select *
from Students as s
left outer join Tests as t
on s.StudentId = t.StudentId
The full outer join will show all students with their tests if they have them, and tests even if they do not have students.
select *
from Students as s
full outer join Tests as t
on s.StudentId = t.StudentId

Oracle - select statement to rollup multiple tables within a time frame

I have 3 Oracle tables for a project that link a demo Transaction table to Transaction_Customer and Transaction_Employee as shown below. Each transaction can have multiple customers involved and many employees involved.
I am trying to write a SQL query which will list each Customer_ID that has had transactions with multiple employees within a one period. I would like the output to include a single row for each Customer_ID with a comma separated list of which Employee_IDs had a transaction with that customer.
The output should look like this:
Customer_ID|Employees
601|007,008,009
The basic query to join the tables together looks like this:
select * from transactions t
left join transactions_customer tc
on t.t_id = tc.t_id
left join transactions_employee te
on t.t_id = te.t_id
How do I get this do I finish this assignment and get the query working the way intended?
Thank you!
Transactions
T_ID|Date|Amount
1|1/10/2017|100
2|1/10/2017|200
3|1/31/2017|150
4|2/16/2017|175
5|2/17/2017|175
6|2/18/2017|185
Transactions_Customer
T_ID|Customer_ID
1|600
1|601
1|602
2|605
3|606
4|601
5|607
6|607
Transactions_Employee
T_ID|Employee_ID
1|007
1|008
2|009
3|008
4|009
5|007
6|007
Is this what you want?
select tc.Customer_id,
listagg(te.employee_id, ',') within group (order by te.employee_id) as employees
from Transactions_Customer tc join
Transactions_Employee te
on tc.t_id = te.t_id
group by tc.Customer_id;
You only need the Transactions table for filtering on the date. Your question alludes to such filtering but does not exactly describe it, so I left it out.
Edit:
The customer data (and perhaps the employees data too) has duplicates. To avoid these in the output:
select tc.Customer_id,
listagg(te.employee_id, ',') within group (order by te.employee_id) as employees
from (select distinct tc.t_id, tc.customer_id
from Transactions_Customer tc
) tc join
(select distinct te.t_id, te.employee_id
from Transactions_Employee te
) te
on tc.t_id = te.t_id
group by tc.Customer_id;

How to include "zero" / "0" results in COUNT aggregate?

I've just got myself a little bit stuck with some SQL. I don't think I can phrase the question brilliantly - so let me show you.
I have two tables, one called person, one called appointment. I'm trying to return the number of appointments a person has (including if they have zero). Appointment contains the person_id and there is a person_id per appointment. So COUNT(person_id) is a sensible approach.
The query:
SELECT person_id, COUNT(person_id) AS "number_of_appointments"
FROM appointment
GROUP BY person_id;
Will return correctly, the number of appointments a person_id has. However, a person who has 0 appointments isn't returned (obviously as they are not in that table).
Tweaking the statement to take person_id from the person table gives me something like:
SELECT person.person_id, COUNT(appointment.person_id) AS "number_of_appointments"
FROM appointment
JOIN person ON person.person_id = appointment.person_id
GROUP BY person.person_id;
This however, will still only return a person_id who has an appointment and not what I want which is a return with persons who have 0 appointments!
Any suggestions please?
You want an outer join for this (and you need to use person as the "driving" table)
SELECT person.person_id, COUNT(appointment.person_id) AS "number_of_appointments"
FROM person
LEFT JOIN appointment ON person.person_id = appointment.person_id
GROUP BY person.person_id;
The reason why this is working, is that the outer (left) join will return NULL for those persons that do not have an appointment. The aggregate function count() will not count NULL values and thus you'll not get a zero.
If you want to learn more about outer joins, here is a nice tutorial: http://sqlzoo.net/wiki/Using_Null
You must use LEFT JOIN instead of INNER JOIN
SELECT person.person_id, COUNT(appointment.person_id) AS "number_of_appointments"
FROM person
LEFT JOIN appointment ON person.person_id = appointment.person_id
GROUP BY person.person_id;
if you do the outer join (with the count), and then use this result as a sub-table, you can get 0 as expected (thanks to the nvl function)
Ex:
select P.person_id, nvl(A.nb_apptmts, 0) from
(SELECT person.person_id
FROM person) P
LEFT JOIN
(select person_id, count(*) as nb_apptmts
from appointment
group by person_id) A
ON P.person_id = A.person_id
USE join to get 0 count in the result using GROUP BY.
simply 'join' does Inner join in MS SQL so , Go for left or right join.
If the table which contains the primary key is mentioned first in the QUERY then use LEFT join else RIGHT join.
EG:
select WARDNO,count(WARDCODE) from MAIPADH
right join MSWARDH on MSWARDH.WARDNO= MAIPADH.WARDCODE
group by WARDNO
.
select WARDNO,count(WARDCODE) from MSWARDH
left join MAIPADH on MSWARDH.WARDNO= MAIPADH.WARDCODE group by WARDNO
Take group by from the table which has Primary key and count from the another table which has actual entries/details.
To change even less on your original query, you can turn your join into a RIGHT join
SELECT person.person_id, COUNT(appointment.person_id) AS "number_of_appointments"
FROM appointment
RIGHT JOIN person ON person.person_id = appointment.person_id
GROUP BY person.person_id;
This just builds on the selected answer, but as the outer join is in the RIGHT direction, only one word needs to be added and less changes. - Just remember that it's there and can sometimes make queries more readable and require less rebuilding.
The problem with a LEFT JOIN is that if there are no appointments, it will still return one row with a null, which when aggregated by COUNT will become 1, and it will appear that the person has one appointment when actually they have none. I think this will give the correct results:
SELECT person.person_id,
(SELECT COUNT(*) FROM appointment WHERE person.person_id = appointment.person_id) AS 'Appointments'
FROM person;