Query using subqueries in where statement - sql

I have a query with subqueries in the where statement which works fine but I need to improve on it but I am not sure of the best way to go about this.
Basically I am getting last years results for students in this years classes. The code below works fine for classcode '10DRADRA1' but there are many more classes and I want the query to iterate through all classes for 2017 Semester 1. I need to have the classcode included as one of the output fields also. So the query will start with the first class and give all the results for that class, then do the same for the next class in the subquery. I am not sure how to include the classcode in the select statement next to all the results from the previous year.
Note that students may do a number of classes this year so there may be some repetition of the results from the previous year. Every class they do this year should include the students name and their results from the previous year. I hardcoded '10DRADRA1' in just to get things working, if I remove it it provides all students in the school only once, while I want each class they are in once and the same results for every class they are in.
Also not that vStudentReportsSemesterResults is a view that holds all results, StudentClasses is a table which holds the class codes a student is doing and SubjectClasses is a table holding the codes for all classes in the school
Is anyone able to advise on the best way of doing this? Here is my current code.
SELECT vStudentReportsSemesterResults.StudentID,
vStudentReportsSemesterResults.AssessResultsResult,
vStudentReportsSemesterResults.AssessAreaHdgAbbrev2,
vStudentReportsSemesterResults.FileSemester,
vStudentReportsSemesterResults.FileYear,
vStudentReportsSemesterResults.ClassLearningAreaCode,
vStudentReportsSemesterResults.AssessmentCode,
vStudentReportsSemesterResults.StudentNameInternal
FROM vStudentReportsSemesterResults
WHERE vStudentReportsSemesterResults.StudentID in
(
select StudentClasses.ID from StudentClasses
where StudentClasses.filesemester = 1 and
StudentClasses.fileyear = 2017 and
StudentClasses.classcode in
(
select SubjectClasses.ClassCode from SubjectClasses
where SubjectClasses.FileYear = 2017 and
SubjectClasses.FileSemester = 1 and
SubjectClasses.FileType = 'A' and
SubjectClasses.ClassCampus = 'S' and
SubjectClasses.ClassCode like '10DRADRA1'
)
)
and (vStudentReportsSemesterResults.ClassLearningAreaCode = 'ENG' OR
vStudentReportsSemesterResults.ClassLearningAreaCode = 'MAT' OR
vStudentReportsSemesterResults.ClassLearningAreaCode = 'SCI') AND
(vStudentReportsSemesterResults.AssessAreaHdgAbbrev2 = 'Grade' OR
vStudentReportsSemesterResults.AssessAreaHdgAbbrev2 = 'Level') AND
(vStudentReportsSemesterResults.AssessResultsResult <> '') AND
(vStudentReportsSemesterResults.FileYear = 2016) AND
(vStudentReportsSemesterResults.FileSemester = 4)

Join StudentClasses to vStudentReportsSemesterResults:
SELECT
sc.ClassCode,
srsr.StudentID,
srsr.AssessResultsResult,
srsr.AssessAreaHdgAbbrev2,
srsr.FileSemester,
srsr.FileYear,
srsr.ClassLearningAreaCode,
srsr.AssessmentCode,
srsr.StudentNameInternal
FROM vStudentReportsSemesterResults srsr
JOIN StudentClasses sc
ON sc.ID = srsr.StudentID
AND sc.FileSemester = 1
AND sc.FileYear = srsr.FileYear + 1
AND EXISTS
(
select *
from SubjectClasses subc
where subc.FileYear = sc.FileYear
and subc.ClassCode = sc.ClassCode
and subc.FileSemester = sc.FileSemester
and subc.FileType = 'A'
and subc.ClassCampus = 'S'
)
WHERE srsr.ClassLearningAreaCode in ('ENG', 'MAT', 'SCI')
AND srsr.AssessAreaHdgAbbrev2 in ('Grade', 'Level')
AND srsr.AssessResultsResult <> ''
AND srsr.FileYear = 2016
AND srsr.FileSemester = 4;

This is more readable:
SELECT
ClassCode,
StudentID,
AssessResultsResult,
AssessAreaHdgAbbrev2,
ClassLearningAreaCode,
AssessmentCode,
StudentNameInternal,
lr.FileYear,
lr.FileSemester,
tr.FileYear,
tr.FileSemester,
tr.FileType,
tr.ClassCampus
FROM
vStudentReportsSemesterResults lr
inner join (
select ID, sc.ClassCode, sc.FileYear, sc.filesemester, sbjc.FileType, sbjc.ClassCampus
from StudentClasses sc
INNER JOIN SubjectClasses sbjc ON SC.classcode = sbjc.ClassCode and sc.FileSemester=sbjc.FileSemester and sc.FileYear=sbjc.FileYear
) tr on lr.StudentID = tr.ID
and lr.FileYear = tr.FileYear - 1
WHERE
ClassLearningAreaCode IN ('ENG', 'MAT', 'SCI')
AND AssessAreaHdgAbbrev2 IN ('Grade', 'Level')
AND (AssessResultsResult <> '')
AND lr.FileYear = 2016
AND lr.FileSemester = 4
and tr.FileSemester = 1
and tr.FileType = 'A'
and tr.ClassCampus = 'S'
ORDER BY 2,1,5
In SELECT section I have added some fields to better understand result.
In WHERE clause you can control how to filter comparision.
Put some indexes on join and filter columns and the query will run fast

Related

Sorry I need to hide

Elon Reeve Musk FRS is an entrepreneur and business magnate. He is the founder, CEO, and Chief Engineer at SpaceX; early-stage investor, CEO, and Product Architect of Tesla, Inc.; founder of The Boring Company; and co-founder of Neuralink and OpenAI.
Your inner select returns a table. That can't be used as parameter to match a WHERE IN condition. Instead try using an INNER JOIN
sum(decode(
select sum(dou.noukn)
from dou
join v_kzeiritsu on
dou.zeiritsu = v_kzeiritsu.zeiritsu
)) as noukn2;
Just move your sum logic inside select as follows:
(SELECT SUM(DOU$2.NOUKN)
FROM SDNISHI.V_KZEIRITSU V
WHERE DOU$2.ZEIRITSU = V.ZEIRITSU) AS NOUKN2
In case If it gives aggregation error then use sum(above query) AS NOUKN2
Your code is very strange. For instance, it seems to assume that V_KZEIRITSU has one row. But, you can move this to the FROM clause:
SELECT SUM(CASE WHEN DOU.ZEIRITSU = K.ZEIRITSU THEN DOU.NOUKN ELSE 0 END) AS NOUKN2
FROM DOU LEFT JOIN
V_KZEIRITSU K
ON 1=1 -- in case the table is really empty
A slightly more reasonable version would be:
SELECT SUM(DOU.NOUKN) AS NOUKN2
FROM DOU LEFT JOIN
V_KZEIRITSU K
ON DOU.ZEIRITSU = K.ZEIRITSU -- in case the table is really empty
It seems rather unlikely to me that this is what you really intend. If not, I would suggest that you ask a new question with appropriate same data, desired results, and explanation of the results. A non-working query should not be expected to provide the same level of information.
I'd say that it is, actually, as simple as
select sum(dou.noukn)
from dou
where dou.zeiritsu in (select zeiritsu from v_kzeiritsu)
(I'm not sure what dou is (table? alias?), but I hope you do.)
After you edited the question, I'm editing the answer. I marked with "--> this" two lines that - in my opinion - might help. As previously, the whole sum(case ...) as noukn2 is replaced by a simple sum(dou$2.noukn).
Note that - in Oracle - you can't use as keyword for table alias. For example:
no: from employees as e
yes: from employees e
Here's your query:
SELECT DOU$2.CUSTCD AS CUSTCD,
DOU$2.CHUNO AS CHUNO,
DOU$2.LINNO AS LINNO,
DOU$2.SHIPDAYYM AS SHIPDAYYM,
SUM (DOU$2.NOUKN) AS NOUKN,
SUM (DOU$2.ZEIKN) AS ZEIKN,
SUM (dou$2.noukn) AS noukn2 --> this
FROM SDNISHI.T_HCHUMON_DOUSOU DOU$2
INNER JOIN SDNISHI.SY_KANRI KNR ON KNR.SHIPDAYYM = DOU$2.SHIPDAYYM
INNER JOIN SDNISHI.T_HCHUMON_MEI MEI
ON MEI.CUSTCD = DOU$2.CUSTCD
AND MEI.CHUNO = DOU$2.CHUNO
AND MEI.LINNO = DOU$2.LINNO
AND MEI.SHIPDAYYM = DOU$2.SHIPDAYYM
AND MEI.USEDNGKBN = '0'
AND MEI.CANCELKBN = '0'
LEFT OUTER JOIN SDNISHI.T_HCHUMON_HD HD
ON HD.CUSTCD = MEI.CUSTCD
AND HD.CHUNO = MEI.CHUNO
AND HD.LINNO = MEI.LINNO
AND HD.USEDNGKBN = '0'
AND HD.CANCELKBN = '0'
AND isnull (HD.CANKBN, '00') = '00'
JOIN v_keziritsu vk ON vk.zeiritsu = dou$2.zeiritsu --> this
WHERE DOU$2.USEDNGKBN = '0'
AND DOU$2.CANCELKBN = '0'
AND ( ( MEI.CHGDELKBN = '1'
AND MEI.HDOUSOUKBN = '02'
AND ( MEI.CHUSU > 0
OR MEI.BCHUSU > 0))
OR ( MEI.CHGDELKBN != '1'
AND HD.HDOUSOUKBN = '02'
AND ( MEI.CHKBTNFGA = '1'
AND HD.CHUSU > 0)
OR ( MEI.CHKBTNFGB = '1'
AND HD.BCHUSU > 0)))
GROUP BY DOU$2.CUSTCD,
DOU$2.CHUNO,
DOU$2.LINNO,
DOU$2.SHIPDAYYM

Summing using a case expression

I am looking to roll up my numbers.
SELECT
SORDERQ.SOHNUM_0,
YQTYORD_0,
ORDINVNOT_0
FROM LIVE.SORDER
LEFT JOIN LIVE. SORDERQ ON SORDER.SOHNUM_0 = SORDERQ.SOHNUM_0
WHERE SORDER.SOHNUM_0 = 'SC111-162420_19'
AND ZBPSELECTION_0 <> ''
AND YCROPYR_0 = '2019'
AND SORDER.SALFCY_0 = '111'
I want to return 1 record per SOHNUM_0,the sum of YQTYORD_0 by SOHNUM_0 and ORDINVNOT_0.
I want to return 1 record per SOHNUM_0,the sum of YQTYORD_0 by SOHNUM_0 and ORDINVNOT_0.
Are you just looking for simple aggregation?
SELECT
q.SOHNUM_0,
SUM(YQTYORD_0) SUM_YQTYORD_0,
ORDINVNOT_0
FROM LIVE.SORDER o
LEFT JOIN LIVE.SORDERQ q ON o.SOHNUM_0 = q.SOHNUM_0
WHERE
o.SOHNUM_0 = 'SC111-162420_19'
AND o.SALFCY_0 = '111'
AND ZBPSELECTION_0 <> ''
AND YCROPYR_0 = '2019'
GROUP BY
q.SOHNUM_0,
ORDINVNOT_0
Note:
I modified your query so it uses table aliases - this makes it shorter
you should prefix all columns in the query with the table they belong to, to make your query unmabiguous and easier to understand

Trying to select all records where a change has been made from the previous year

I have a table of courses: c_inst, c_year, c_program_type, c_unique_key. I want to find where the c_program_type has changed from 'V' to 'P' and vice versa from 2016 to 2017. I have having issues getting this to run. Here is what i've tried and I know it's just flat out wrong:
select * from courses a
where c_program_type in ('P','V')
and c_year = '2017'
and c_program_type != (select c_program_type from courses b where
a.c_unique_key = b.c_unique_key and c_year = '2016')
What is the easiest way to do this for a sql noob? I've also tried using sql server temp tables, but haven't had luck there. Thanks in advance!
You can try
SELECT a.*
FROM courses a
LEFT JOIN courses b
ON a.c_unique_key = b.c_unique_key
AND a.c_program_type = b.c_program_type
AND b.c_year = '2016'
WHERE a.c_program_type IN ( 'P', 'V' )
AND a.c_year = '2017'
AND b.c_unique_key IS NULL

Only return value that matches the ID on table 1

I have tried all possible joins and sub-queries but I cant get the data to only return one value from table 2 that exactly matches the vendor ID. If I dont have the address included in the query, I get one hit for the vendor ID. How can I make it so that when I add the address, I only want the one vendor that I get prior to adding the address.
The vendor from table one must be VEN-CLASS IS NOT NULL.
This was my last attempt using subquery:
SELECT DISTINCT APVENMAST.VENDOR_GROUP,
APVENMAST.VENDOR,
APVENMAST.VENDOR_VNAME,
APVENMAST.VENDOR_CONTCT,
APVENMAST.TAX_ID,
Subquery.ADDR1
FROM (TEST.dbo.APVENMAST APVENMAST
INNER JOIN
(SELECT APVENADDR.ADDR1,
APVENADDR.VENDOR_GROUP,
APVENADDR.VENDOR,
APVENMAST.VEN_CLASS
FROM TEST.dbo.APVENADDR APVENADDR
INNER JOIN TEST.dbo.APVENMAST APVENMAST
ON (APVENADDR.VENDOR_GROUP = APVENMAST.VENDOR_GROUP)
AND (APVENADDR.VENDOR = APVENMAST.VENDOR)
WHERE (APVENMAST.VEN_CLASS IS NOT NULL)) Subquery
ON (APVENMAST.VENDOR_GROUP = Subquery.VENDOR_GROUP)
AND (APVENMAST.VENDOR = Subquery.VENDOR))
INNER JOIN TEST.dbo.APVENLOC APVENLOC
ON (APVENMAST.VENDOR_GROUP = APVENLOC.VENDOR_GROUP)
AND (APVENMAST.VENDOR = APVENLOC.VENDOR)
WHERE (APVENMAST.VEN_CLASS IS NOT NULL)
Try this:
SELECT APVENMAST.VENDOR_GROUP
, APVENMAST.VENDOR
, APVENMAST.VENDOR_VNAME
, APVENMAST.VENDOR_CONTCT
, APVENMAST.TAX_ID
, APVENADDR.ADDR1
FROM TEST.dbo.APVENMAST APVENMAST
INNER JOIN (
select VENDOR_GROUP, VENDOR, ADDR1
, row_number() over (partition by VENDOR_GROUP, VENDOR order by ADDR1) r
from TEST.dbo.APVENADDR
) APVENADDR
ON APVENADDR.VENDOR_GROUP = APVENMAST.VENDOR_GROUP
AND APVENADDR.VENDOR = APVENMAST.VENDOR
AND APVENADDR.r = 1
--do you need this table; you're not using it...
--INNER JOIN TEST.dbo.APVENLOC APVENLOC
--ON APVENMAST.VENDOR_GROUP = APVENLOC.VENDOR_GROUP
--AND APVENMAST.VENDOR = APVENLOC.VENDOR
WHERE APVENMAST.VEN_CLASS IS NOT NULL
--if the above inner join was to filter results, you can do this instead:
and exists (
select top 1 1
from TEST.dbo.APVENLOC APVENLOC
ON APVENMAST.VENDOR_GROUP = APVENLOC.VENDOR_GROUP
AND APVENMAST.VENDOR = APVENLOC.VENDOR
)
I found another column in the APVENLOC table that I can filter on to get the unique vendor. Turns out if the vendor address is for the main office, the vendor location is set blank.
Easier than I thought it would be!
SELECT DISTINCT APVENMAST.VENDOR_GROUP,
APVENMAST.VENDOR,
APVENMAST.VENDOR_VNAME,
APVENADDR.ADDR1,
APVENMAST.VENDOR_SNAME,
APVENADDR.LOCATION_CODE,
APVENMAST.VEN_CLASS
FROM TEST.dbo.APVENMAST APVENMAST
INNER JOIN TEST.dbo.APVENADDR APVENADDR
ON (APVENMAST.VENDOR_GROUP = APVENADDR.VENDOR_GROUP)
AND (APVENMAST.VENDOR = APVENADDR.VENDOR)
WHERE (APVENADDR.LOCATION_CODE = ' ')
Shaji

Django raw sql produces a queryset I can't iterate - CLOSED

I've got a sql select query which returns two rows:
SELECT contacts_patientcontact.contact_id, patient_firstname, recent_mailouts
FROM contacts_patientcontact
INNER JOIN patients_patientcore
ON contacts_patientcontact.patient_id = patients_patientcore.patient_id
LEFT JOIN (SELECT contact_id, COUNT(*) as recent_mailouts
FROM contacts_communicationinstance
WHERE communication_type = 'questionnaire mailout'
GROUP BY contact_id) mail_outs
ON contacts_patientcontact.contact_id = mail_outs.contact_id
WHERE contact_date BETWEEN '2012/03/05' AND '2012/03/12'
AND contact_type = 'Postal Questionnaire'
AND patient_dead != 1
AND consent_withdrawn IS NULL
AND lost_follow_up != 1
AND (key = 'A' OR key = 'C')
AND (recent_mailouts < 1
OR recent_mailouts IS NULL);
However when I add it into django using the raw method the queryset doesn't seem to be iterable.
def weekly_questionnaire_mailout_query(monday):
"""
Returns a query set of PatientContact objects for patients
due a mailout in the week following the parameter 'monday'.
"""
nxt_monday = monday + datetime.timedelta(weeks=1)
nxt_monday_str = nxt_monday.strftime('%Y/%m/%d')
monday_str = monday.strftime('%Y/%m/%d')
contacts = PatientContact.objects.raw("""
SELECT contacts_patientcontact.contact_id
FROM contacts_patientcontact
INNER JOIN patients_patientcore
ON contacts_patientcontact.patient_id = patients_patientcore.patient_id
LEFT JOIN (SELECT contact_id, COUNT(*) as recent_mailouts
FROM contacts_communicationinstance
WHERE communication_type = 'questionnaire mailout'
GROUP BY contact_id) mail_outs
ON contacts_patientcontact.contact_id = mail_outs.contact_id
WHERE contact_date BETWEEN '%s' AND '%s'
AND contact_type = 'Postal Questionnaire'
AND patient_dead != 1
AND consent_withdrawn IS NULL
AND lost_follow_up != 1
AND (cora = 'A' OR cora = 'C')
AND (recent_mailouts < 1
OR recent_mailouts IS NULL);
""" % (monday_str, nxt_monday_str)
)
return contacts
contacts = weekly_questionnaire_mailout_query(monday)
for contact in contacts:
patients.add(contact.patient_id)
That last line is never reached. (I've checked the dates are correct, and I've included the PatientContact model below).
class PatientContact(models.Model):
contact_id = models.AutoField(primary_key=True)
patient_id = models.ForeignKey(PatientCore, db_column="patient_id",
verbose_name="patient")
# additional fields..
I'm at a loss with this - instead of showing the items in a queryset my (pydevd) debugger shows a RawQuerySet object. The same function (with the same parameter) is returning an object that djangotables2 handles fine (producing the table I'd expect from the sql output).
EDIT
That's embarrassing - it was the dates after all - I wasn't actually running the same SQL query (I thought I'd checked and rechecked them last week). Apologies to anyone who's spent any time on this.
This was a mistake. I was running the wrong piece of code.
The solution is to be more carefull!