How does Ellucian Banner calculate GPA? - sql

I am working with Ellucian Banner, and I am having a hard time calculating the GPA. (Banner is an ERP used at academic institutions.)
The GPA can be found in many of the views that are built into Banner, including AS_STUDENT_DATA. However, these views are very slow and we have a few reports that are run several times where only the GPA is needed. I am trying to extract the GPA, but the values that I am getting don't all match what is in the views.
Please note that I am able to calculate a GPA using one of many sources on the web, however my values don't perfectly match values in Banner views. (In other words, I am not asking how to calculate a general GPA, which is easy and well documented, but asking how this is done in Banner.)
I have this, which gives values that are close but not all correct:
SELECT PIDM,
round(sum(TOTAL_POINTS)/sum(TOTAL_CREDITS), 2) AS GPA,
round(TOTAL_POINTS, 2) AS TOTAL_POINTS, TOTAL_CREDITS, LEVL_CODE
FROM (
SELECT
SFRSTCR.SFRSTCR_PIDM AS PIDM,
sum(SHRGRDE.SHRGRDE_QUALITY_POINTS * SFRSTCR.SFRSTCR_CREDIT_HR) AS TOTAL_POINTS,
sum(SFRSTCR.SFRSTCR_CREDIT_HR) AS TOTAL_CREDITS,
SHRGRDE_LEVL_CODE AS LEVL_CODE
FROM
INNER JOIN SHRGRDE ON SFRSTCR.SFRSTCR_GRDE_CODE = SHRGRDE.SHRGRDE_CODE AND SHRGRDE.SHRGRDE_GPA_IND = 'Y'
WHERE SHRGRDE_GPA_IND = 'Y'
AND SFRSTCR.SFRSTCR_RSTS_CODE IN ('RE', 'RW', 'RR')
GROUP BY SFRSTCR.SFRSTCR_PIDM, SHRGRDE_LEVL_CODE -- , SFRSTCR.SFRSTCR_CRN, SFRSTCR_TERM_CODE
) GT
WHERE GT.TOTAL_CREDITS > 0 -- Prevent x/0 errors
GROUP BY PIDM, TOTAL_POINTS, TOTAL_CREDITS, LEVL_CODE
Has anyone tackled this problem? Any idea how Banner does it?

you can use the built-in function for banner. its under a package called SHKSELS. the function is called F_SHRLGPA_VALUE. the owner of SHKSELS is BANINST1. the inputs for the function are pidm, credit level, indicator type, GPA type, type of request, campus type, term.
here is a break down and then an example use.
input 1 - pidm --self explanatory
input 2 - credit level value -- options are found by using
select * from stvlevl;
input 3 - indicator type -- Options are GPA (calculated GPA) or QP (Quality Points)
input 4 - GPA Type -- Options are found using
select distinct shrlgpa_gpa_type_ind from shrlgpa;
input 5 - type of request -- Options are V (value of input 3) or HA (Hours Attempted) or HE (Hours Earned) or HP (Hours Passed) or H (Hours toward GPA)
input 6 - campus type -- options are found by using
select * from stvcamp;
input 7 - term -- self explanatory
Most inputs can be NULL if you dont want to be that specific.
EXAMPLE:
SELECT SPRIDEN_ID as IS_NUMBER,
SHKSELS.F_SHRLGPA_VALUE(SPRIDEN_PIDM,'01','GPA','I','V',NULL,NULL) as GPA
FROM SPRIDEN
WHERE SPRIDEN_CHANGE_IND IS NULL;
Hope that helps.

Over release banner8..x if the real or final GPA already was calculated, then it was got the final grades from Academic History tables ( SHRTCKN, SHRTCKG, SHRTCKL) then you can get the GPA from SHRTGPA and SHRLGPA tables (calculated at term and level respectly)
If you need to recalculates the GPA then you will use the
shkcgpa.p_term_gpa with pidm and term by parameters. Therefore both GPA are recalculated.

Here's a guess. Hopefully it's closer.
SELECT
PIDM, LEVL_CODE,
round(sum(TOTAL_POINTS) / sum(TOTAL_CREDITS), 2) AS GPA,
sum(TOTAL_POINTS) AS TOTAL_POINTS, sum(TOTAL_CREDITS) AS TOTAL_CREDITS
FROM (
SELECT
SFRSTCR.SFRSTCR_PIDM AS PIDM, SHRGRDE_LEVL_CODE AS LEVL_CODE,
sum(SHRGRDE.SHRGRDE_QUALITY_POINTS * SFRSTCR.SFRSTCR_CREDIT_HR) AS TOTAL_POINTS,
sum(SFRSTCR.SFRSTCR_CREDIT_HR) AS TOTAL_CREDITS
FROM
SFRSTCR INNER JOIN SHRGRDE ON SFRSTCR.SFRSTCR_GRDE_CODE = SHRGRDE.SHRGRDE_CODE
WHERE
SHRGRDE_GPA_IND = 'Y' AND SFRSTCR.SFRSTCR_RSTS_CODE IN ('RE', 'RW', 'RR')
GROUP BY
SFRSTCR.SFRSTCR_PIDM, SHRGRDE_LEVL_CODE
) GT
WHERE TOTAL_CREDITS > 0 -- Prevent x/0 errors
GROUP BY PIDM, LEVL_CODE

Related

SQL query to return average of results using JOIN and GROUP BY

I have a simple manufacturing job card system that track parts and labor for an assigned job.
It consists of a JobHeader table that holds the Job Card number (JobHeader.JobNo), ID of the part being manufactured (JobHeader.RegNo) and quantity to be manufactured (JobHeader.RegNo).
There is a child table (JobLabour) that tracks all the times that have been worked on the job (JobLabour.WorkedTime)
I'm looking for a query that will return the average time taken to produce a part accross the last 5 job cards for that particular part.
The following query
SELECT TOP 5 JobHeader.RegNo, JobHeader.BOMQty, sum(JobLabour.WorkedTime) AS TotalTime FROM JobHeader INNER JOIN JobLabour ON JobHeader.JobNo=JobLabour.JobNo
WHERE JobHeader.RegNo='RM-BRU-0134'
GROUP BY JobHeader.BOMQty, JobHeader.JobNo, JobHeader.RegNo
will return this:
But what I'm looking for is a query that will return the average BOMQty and average totalTime. Something like this:
Is there a way to do this?
Your question explicitly mentions the "last five" but does not specify how that is determined. Presumably, you have some sort of date/time column in the data that defines this.
In SQL Server, you can use apply:
select jh.*, jl.*
from jobheader jh outer apply
(select top (5) avg(BOMQty) as avg_BOMQty, avg(totalTime) as avg_totalTime
from (select top (5) jl.*
from joblabour jl
where jl.regno = jh.regno
order by jl.<some datetime> -- however you determine the last five
) jl
) jl;
You can add a where clause to the outer query to filter on one or more particular jobs.
If I understand you correctly this will do the work
this will work for 1 RegNo='RM-BRU-0134' at a time
with topFive as (
SELECT TOP 5 JobHeader.RegNo, JobHeader.BOMQty, sum(JobLabour.WorkedTime) AS TotalTime
FROM JobHeader
INNER JOIN JobLabour ON JobHeader.JobNo = JobLabour.JobNo
WHERE JobHeader.RegNo = 'RM-BRU-0134'
GROUP BY JobHeader.BOMQty, JobHeader.JobNo, JobHeader.RegNo
)
select RegNo, avg(BOMQty) as BOMQty, avg(TotalTime) as TotalTime
from topFive
group by RegNo

How to compare two tables, in order to calculate the growth rate between two time periods?

The table I currently have looks like this:
(data comes from two different tables, 19921231, 19930331)
The table I want to create looks like this (5th column added)
Goal: determine the deposit growth rate at each bank. I.e. Comparing the amount of deposits held at a bank in the previous quarter (e.g. 19921231) to the deposits of the most recent quarter (e.g. 19930331). Then view the increase/decrease as a percentage.
This is the code I have written so far:
select
AL.repdte as `Date`, AL.cert, AL.name, AL.dep as `Deposits`
FROM usa_fdic_call_reports_1992.All_Reports_19921231_Assets_and_Liabilities as AL
UNION ALL
select
AL.repdte as `Date`, AL.cert, AL.name, AL.dep as `Deposits`
FROM usa_fdic_call_reports_1993.All_Reports_19930331_Assets_and_Liabilities as AL
An answer to this question suggested this code, that works
However, for some reason I getting an output of "NULL"
select al19930331.repdte as `Date`, al19930331.cert, al19930331.name,
al19930331.dep as Deposits_1993,
al19921231.dep as Deposits_1992,
(al19930331.dep - al19921231.dep) / al19921231.dep as grow_rate
from usa_fdic_call_reports_1993.All_Reports_19930331_Assets_and_Liabilities as al19930331 left join
usa_fdic_call_reports_1992.All_Reports_19921231_Assets_and_Liabilities as al19921231
on al19930331.cert = al19921231.cert and
al19930331.name = al19921231.name and
al19921231.repdte = date_add(al19930331.repdte, interval 1 year);
In an attempt to isolate the "NULL" issue, I reduced the query to it's simplest terms, and was able to eliminate the "NULL" issue.
Now we have deposit columns for both quarters returning what appears to be proper outputs.
Next I removed the last line of code from:
select al19930331.repdte as `Date`, al19930331.cert, al19930331.name,
al19930331.dep as Deposits_1993,
al19921231.dep as Deposits_1992,
(al19930331.dep - al19921231.dep) / al19921231.dep as grow_rate
from usa_fdic_call_reports_1993.All_Reports_19930331_Assets_and_Liabilities as al19930331 left join
usa_fdic_call_reports_1992.All_Reports_19921231_Assets_and_Liabilities as al19921231
on al19930331.cert = al19921231.cert and
al19930331.name = al19921231.name and
al19921231.repdte = date_add(al19930331.repdte, interval 1 year);
Removing the last line of code worked, sort of. Running the code produces a "division by zero" error. How can one eliminate the division by zero error?
select al19930331.repdte as `Date`, al19930331.cert, al19930331.name,
al19930331.dep as Deposits_1993,
al19921231.dep as Deposits_1992,
(al19930331.dep - al19921231.dep) / al19921231.dep as grow_rate
from usa_fdic_call_reports_1993.All_Reports_19930331_Assets_and_Liabilities as al19930331 left join
usa_fdic_call_reports_1992.All_Reports_19921231_Assets_and_Liabilities as al19921231
on al19930331.cert = al19921231.cert and
al19930331.name = al19921231.name
You should not be storing this information in different tables. This should all be in the same table using different partitions. But with the date embedded in the middle of the name, I think you need to use an explicit join:
select al19930331.repdte as `Date`, al19930331.cert, al19930331.name,
al19930331.dep as Deposits_1993_0331,
al19921231.dep as Deposits_1992_1231,
(al19930331.dep - al19921231.dep) / al19921231.dep as grow_rate
from usa_fdic_call_reports_1993.All_Reports_19930331_Assets_and_Liabilities as al19930331 left join
usa_fdic_call_reports_1992.All_Reports_19921231_Assets_and_Liabilities as al19921231
on al19930331.cert = al19921231.cert and
al19921231.repdte = date_add(al19930331.repdte, interval 1 quarter);
This would be simpler with the data in one table.
You can use lag():
select
t.*,
100.0
* (deposits - lag(deposits) over(partition by cert order by depdte))
/ lag(deposits) over(partition by cert order by depdte) as growth_percent
from mytable t

mSSQL, SQL view, select, percentage query

So this is the requirement I need to meet:
Aggregated data of all the schools in the ESD, grouped by
SchoolDistrict.SchoolDistrictID
(get the same data as the school district scenario above, then add grouping by district, filtered to
EducationServiceDistrict. EducationServiceDistrictID
)
Also calculate percentage of pass, fail, and untested
How do I calculate the percentage pass, fail, and untested?
This is the query I have written so far.
CREATE VIEW district_map AS
SELECT * and SchoolDistrictID,
EducationServiceDistrict
FROM SchoolDistrict_View
and SchoolDistrict,
EducationServiceDistrict
GROUP BY EducationServiceDistrict.EducationServiceDistrictID
ORDER BYLeadWaterTestLocation.PassFail
This is the general idea of how these problems are solved - if you understand this simplified version you will be able to solve your problem.
select d.districtName,
s.studentCount,
case when s.studentCount=0 then 0 else s.passed / s.studentCount * 100 end as PassedPct
from district d
join (select districtId,
sum(studentCount) as studentCount,
sum(passed) as passed
from schools
group by districtId) as s
on d.districtId = s.districtId
order by d.districtName

Postgresql query for every day sold stock count

I have project on CRM which maintains product sales order for every organization.
I want to count everyday sold stock which I have managed to do by looping over by date but obviously it is a ridiculous method and taking more time and memory.
Please help me to find out it in single query. Is it possible?
Here is my database structure for your reference.
product : id (PK), name
organization : id (PK), name
sales_order : id (PK), product_id (FK), organization_id (FK), sold_stock, sold_date(epoch time)
Expected Output for selected month :
organization | product | day1_sold_stock | day2_sold_stock | ..... | day30_sold_stock
http://sqlfiddle.com/#!15/e1dc3/3
Create tablfunc :
CREATE EXTENSION IF NOT EXISTS tablefunc;
Query :
select "proId" as ProductId ,product_name as ProductName,organizationName as OrganizationName,
coalesce( "1-day",0) as "1-day" ,coalesce( "2-day",0) as "2-day" ,coalesce( "3-day",0) as "3-day" ,
coalesce( "4-day",0) as "4-day" ,coalesce( "5-day",0) as "5-day" ,coalesce( "6-day",0) as "6-day" ,
coalesce( "7-day",0) as "7-day" ,coalesce( "8-day",0) as "8-day" ,coalesce( "9-day",0) as "9-day" ,
coalesce("10-day",0) as "10-day" ,coalesce("11-day",0) as "11-day" ,coalesce("12-day",0) as "12-day" ,
coalesce("13-day",0) as "13-day" ,coalesce("14-day",0) as "14-day" ,coalesce("15-day",0) as"15-day" ,
coalesce("16-day",0) as "16-day" ,coalesce("17-day",0) as "17-day" ,coalesce("18-day",0) as "18-day" ,
coalesce("19-day",0) as "19-day" ,coalesce("20-day",0) as "20-day" ,coalesce("21-day",0) as"21-day" ,
coalesce("22-day",0) as "22-day" ,coalesce("23-day",0) as "23-day" ,coalesce("24-day",0) as "24-day" ,
coalesce("25-day",0) as "25-day" ,coalesce("26-day",0) as "26-day" ,coalesce("27-day",0) as"27-day" ,
coalesce("28-day",0) as "28-day" ,coalesce("29-day",0) as "29-day" ,coalesce("30-day",0) as "30-day" ,
coalesce("31-day",0) as"31-day"
from crosstab(
'select hist.product_id,pr.name,o.name,EXTRACT(day FROM TO_TIMESTAMP(hist.sold_date/1000)),sum(sold_stock)
from sales_order hist
left join product pr on pr.id = hist.product_id
left join organization o on o.id = hist.organization_id
where EXTRACT(MONTH FROM TO_TIMESTAMP(hist.sold_date/1000)) =5
and EXTRACT(YEAR FROM TO_TIMESTAMP(hist.sold_date/1000)) = 2017
group by hist.product_id,pr.name,EXTRACT(day FROM TO_TIMESTAMP(hist.sold_date/1000)),o.name
order by o.name,pr.name',
'select d from generate_series(1,31) d')
as ("proId" int ,product_name text,organizationName text,
"1-day" float,"2-day" float,"3-day" float,"4-day" float,"5-day" float,"6-day" float
,"7-day" float,"8-day" float,"9-day" float,"10-day" float,"11-day" float,"12-day" float,"13-day" float,"14-day" float,"15-day" float,"16-day" float,"17-day" float
,"18-day" float,"19-day" float,"20-day" float,"21-day" float,"22-day" float,"23-day" float,"24-day" float,"25-day" float,"26-day" float,"27-day" float,"28-day" float,
"29-day" float,"30-day" float,"31-day" float);
Please note, use PostgreSQL Crosstab Query. I have used coalesce for handling null values(Crosstab Query to show "0" when there is null data to return).
Following query will help to find the same:
select o.name,
p.name,
sum(case when extract (day from to_timestamp(sold_date))=1 then sold_stock else 0 end)day1_sold_stock,
sum(case when extract (day from to_timestamp(sold_date))=2 then sold_stock else 0 end)day2_sold_stock,
sum(case when extract (day from to_timestamp(sold_date))=3 then sold_stock else 0 end)day3_sold_stock,
from sales_order so,
organization o,
product p
where so.organization_id=o.id
and so.product_id=p.id
group by o.name,
p.name;
I just provided logic to find for 3 days, you can implement the same for rest of the days.
basically first do basic joins on id, and then check if each date(after converting epoch to timestamp and then extract day).
You have a few options here but it is important to understand the limitations first.
The big limitation is that the planner needs to know the record size before the planning stage, so this has to be explicitly defined, not dynamically defined. There are various ways of getting around this. At the end of the day, you are probably going to have somethign like Bavesh's answer, but there are some tools that may help.
Secondly, you may want to aggregate by date in a simple query joining the three tables and then pivot.
For the second approach, you could:
You could do a simple query and then pull the data into Excel or similar and create a pivot table there. This is probably the easiest solution.
You could use the tablefunc extension to create the crosstab for you.
Then we get to the first problem which is that if you are always doing 30 days, then it is easy if tedious. But if you want to do every day for a month, you run into the row length problem. Here what you can do is create a dynamic query in a function (pl/pgsql) and return a refcursor. In this case the actual planning takes place in the function and the planner doesn't need to worry about it on the outer level. Then you call FETCH on the output.

Max of two columns from different tables

I need to get a max of the values from two columns from different tables.
eg the max of suburbs from schoolorder and platterorder. platterorder has clientnumbers that links to normalclient, and schoolorder has clientnumbers that links to school.
I have this:
SELECT MAX (NC.SUBURB) AS SUBURB
FROM normalClient NC
WHERE NC.CLIENTNO IN
(SELECT PO.CLIENTNO
FROM platterOrder PO
WHERE NC.CLIENTNO = PO.CLIENTNO)
GROUP BY NC.SUBURB
UNION
SELECT MAX (S.SUBURB) AS SCHOOLSUBURB
FROM school S
WHERE S.CLIENTNO IN
(SELECT S.CLIENTNO
FROM schoolOrder SO
WHERE S.CLIENTNO = SO.CLIENTNO)
GROUP BY S.SUBURB)
However that gets the max from platter order and joins it with the max of school. what I need is the max of both of them together.
=================================================
sorry for making this so confusing!
the output should only be one row.
it should be the suburb where the maxmimum orders have come from for both normal client and school clients. the orders are listed in platter order for normal clients, and school order for school clients. so it's the maximum value for two table's that don't have a direct relation.
hope that clears it up a bit !
If I'm understanding your question correctly, you don't need to use a GROUP BY since you're wanting the MAX of the field. I've also changed your syntax to use a JOIN instead of IN, but the IN should work just the same:
SELECT MAX (NC.SUBURB) AS SUBURB
FROM normalClient NC
JOIN platterOrder PO ON NC.ClientNo = PO.ClientNo
UNION
SELECT MAX (S.SUBURB) AS SCHOOLSUBURB
FROM school S
JOIN schoolOrder SO ON S.CLIENTNO = SO.CLIENTNO
Withouth knowing your table structures and seeing sample data, the best way I can recommend getting the MAX of results from the UNION is to use a subquery. There may be a better way with JOINs, but it's difficult to infer from your question:
SELECT MAX(Suburb)
FROM (
SELECT MAX (NC.SUBURB) AS SUBURB
FROM normalClient NC
JOIN platterOrder PO ON NC.ClientNo = PO.ClientNo
UNION
SELECT MAX (S.SUBURB)
FROM school S
JOIN schoolOrder SO ON S.CLIENTNO = SO.CLIENTNO
) T