Select Multiple rows into one row in Oracle - oracle

I have a table that stores student results for different exams and different exam types say main exam, continuous assessment, course work etc, I need to query the table such that I get only one row for one particular exam unit with the percentage averaged depending on the number of exams the students sat for. Here is my attempted query:
select stu_reg_no, unit_code,
exam_unit, exam_semester,
student_year,
sum(per_centage_score)/count(per_centage_score) percentage
from student_results_master
group by unit_code, exam_unit,
per_centage_score, stu_reg_no,
exam_semester, student_year;
Here is my resultset:
I have two rows for the same exam unit since one is main exam and the other course work I need my output like this:
E35/1000/2013 TFT001 COMPLEX ANALYSIS 1 1 71.04
E35/1000/2013 TFT002 LINEAR ALGEBRA 1 1 56.25
The percentage for that particular unit is added and divided by the number of exams for that particular unit.
How can I achieve this?

Oracle provides a built-in function for calculating average value for an expression over a set of rows - AVG(). To get the desired output you need to do the following two things:
Replace sum(per_centage_score)/count(per_centage_score) with avg(per_centage_score)
Remove per_centage_score column from the group by clause.
To that end, your query might look like this:
select stu_reg_no
, unit_code
, exam_unit
, exam_semester
, student_year
, avg(percentage) percentage
from student_results_master
group by unit_code
, exam_unit
, stu_reg_no
, exam_semester
, student_year;
Result:
STU_REG_NO UNIT_CODE EXAM_UNIT EXAM_SEMESTER STUDENT_YEAR PERCENTAGE
------------- --------- ---------------- ------------- ------------ ----------
E35/1000/2013 TFT001 COMPLEX ANALYSIS 1 1 71.04
E35/1000/2013 TFT002 LINEAR ALGEBRA 1 1 56.25

try this:
select stu_reg_no, unit_code, exam_unit, exam_semester, student_year,
(select sum(per_centage_score) from student_results_master t2 where t2.exam_unit = t1.exam_unit)
/(select count(per_centage_score) from student_results_master t2 where t2.exam_unit = t1.exam_unit)
from student_results_master t1
group by unit_code, exam_unit, stu_reg_no, exam_semester, student_year;

Related

Google Sheets Query - Arithmetic Query Within 2 Queries

I am new to exploring the capability of Google Query and am seeking some assistance with structuring my query. I am esentially seeking to generate a table similar to the below, that summarises my income + expenses by month, whilst calculating the amount saved each month and the assoicated savings rate.
={query(Expense_Data, "SELECT C, SUM(Q) where T is not null GROUP BY C PIVOT T ORDER BY C desc limit 3",1),query(Expense_Data, "Select Sum(Q) where T is not null Group by C limit 3 label Sum(Q) 'Savings'")}
Month (Date)
Expense
Income
Savings
Savings Rate (NEED ASSISTANCE) %
01/05/2021
-1000.00
1500.00
500.00
33.33%
01/04/2021
-1000.00
1500.00
500.00
33.33%
01/03/2021
-1000.00
1500.00
500.00
33.33%
My dual query below so far does the job for the first 3 columns, however I am unsure how to generate the 'Savings Rate' considering this query would requre (I'm guessing) an arthimetic operator except these would contain different 'where' conditions. (ie SELECT SUM(Q) / SUM(Q) where T = 'Income')??
Any help would be greatly appreciated.
Cheers
You can use the result of your query inside another query
=query(query(Expense_Data, "SELECT C, SUM(Q) where T is not null GROUP BY C PIVOT T ORDER BY C desc limit 3",1),"select Col1,Col2,Col3, Col2+Col3, (Col2+Col3)/Col3 label Col2+Col3'Savings', (Col2+Col3)/Col3'Savings Rate %'")

Oracle 'Partition By' and 'Row_Number' keyword along with pivot

I have this query written by someone else and I am trying to figure out how is it working. I have general idea about all these things such as row_number() , partition by, pivot but I am unable to understand them all together.
For this query :
select
d, p, s, a
from
(
select name,occupation, (ROW_NUMBER() OVER (partition by occupation order by name)) as rownumber from occupations
)
pivot
(
max(name)
for occupation
in ('Doctor' as d, 'Professor' as p, 'Singer' as s, 'Actor' as a)
)
order by rownumber;
This is the input table on which the above query works :
This it the output generated by the query which is correct as per the question :
Jenny Ashley Meera Jane
Samantha Christeen Priya Julia
NULL Ketty NULL Maria
Now, I want to know how the output is generated by the query i.e. step by step with flow of execution. Explanation with easy examples matching the above situation would be much appreciated. Thanks in advance.
After from clause you have following :
select name,occupation, (ROW_NUMBER() OVER (partition by occupation order by name))
Above virtually restack your table data in three columns - Name, occupation, rownumber. rownumber will reset itself as soon as occupation column changes. Output data will be like :
NAME OCCUPATION ROWNUMBER
-------------------- -------------------- ----------
Jane ACTOR 1
Julia ACTOR 2
Maria ACTOR 3
JENNY DOCTOR 1 <-- rownumber reset to 1
Sammantha DOCTOR 2
Pivot function let you aggregate result & rotate rows into columns.
Pivot usage code is :
PIVOT
(
aggregate_function(column2)
FOR column2
IN ( expr1, expr2, ... expr_n) | subquery
)
So your PIVOT function have name stacked NAME based on OCCUPATION . Each stack (column in output) is ordered by rownumber column inserted via first subquery.

Add indicator to top and bottom 10%

I'm trying to capture the average of FIRST_CONTACT_CAL_DAYS but what I would like to do is create an indicator for the top and bottom 10% of values so I can exclude those (outliers) from my average calculation.
Not sure how to go about do this, any thoughts?
SELECT DISTINCT
TO_CHAR(A.FIRST_ASSGN_DT,'DAY') AS DAY_NUMBER,
A.FIRST_ASSGN_DT,
A.FIRST_CONTACT_DT,
TO_CHAR(A.FIRST_CONTACT_DT,'DAY') AS DAY_NUMBER2,
A.FIRST_CONTACT_DT AS FIRST_PHONE_CONTACT,
A.ID,
ABS(TO_DATE(A.FIRST_CONTACT_DT, 'DD/MM/YYYY') - TO_DATE(A.FIRST_ASSGN_DT, 'DD/MM/YYYY')) AS FIRST_CONTACT_CAL_DAYS,
FROM HIST A
LEFT JOIN CONTACTS D ON A.ID = D.ID
WHERE 1=1
You may be looking for something like this. Please adapt to your situation.
I assume you may have more than one "group" or "partition" and you need to compute the average for each group separately, after throwing out the outliers in each partition. (An alternative, which can be easily accommodated by adapting the query below, is to throw out the outliers at the global level, and only then to group and take the average for each group.)
If you don't have any groups, and everything is one big pile of data, it's even easier - you don't need GROUP BY and PARTITION BY.
Then: the function NTILE assigns a bucket number, in this example between 1 and 10, to each row, based on where they fall (first decile, i.e. first 10%, next decile, ... all the way to the last decile). I do this in a subquery. Then in the outer query just filter out the first and last bucket before you group by and you compute the average.
For testing purposes I create three groups with 10,000 random numbers each in a WITH clause - no need to spend any time on that portion of the code, since it is not part of the solution (the SQL code to solve your problem) - it's just a dirty trick to create test data on the fly.
with
inputs ( grp, val ) as (
select ceil(level/10000), dbms_random.value(0, 150)
from dual
connect by level <= 30000
)
select grp, avg(val) as avg_val
from (
select grp, val, ntile(10) over (partition by grp order by val) as bkt
from inputs
)
where bkt between 2 and 9
group by grp
;
GRP AVG_VAL
--- -----------------------
1 75.021614866547043734458
2 74.286117923344418598032
3 75.437412573353736953791

Oracle Query - to sum up the balances on some criteria

Can someone please help me to solve this .. I need to write oracle query to sum up the balances by by REF and REFERENCE_ID
Below are some criteria,
MultiValue can start with 1, 2, 3 and it will have any number of Subvalues from 1 to n...
Now PROPERTY field value is available only for MultiValue=1 and SubValue=1..
we have consider same PROPERTY property field for that multivalue set.
e.g for MultiValue 1 PROPERTY=BALANCE and Multivalue =2 PROPERTY = INTEREST etc...and need to sum up the balances
by REF and REFERENC_ID
Also, need to separate BALANCES amouts and INTEREST amount and PENALTY amount.
Here is some sample data... Any help is appreciated.. Thanks in advance.
Here is the sample output for first two ids..
You can fill in the missing propery values with an analytic first_value() function call (or max, min, etc.):
select reference_id, multivalue, subvalue, code,
first_value(property) over (partition by reference_id, multivalue) as property,
ref, amount
from your_table;
REFERENCE_ID MULTIVALUE SUBVALUE CODE PROPERTY R AMOUNT
-------------- ---------- ---------- ---------- -------- - ----------
BILL121220PBD8 1 1 10001 BALANCE a 1061.08
BILL121220PBD8 1 2 10001 BALANCE b 5395.89
BILL121220PBD8 1 3 10001 BALANCE c 4043.07
BILL121220PBD8 1 4 10001 BALANCE d 4100.22
BILL121220R2HL 2 1 10001 INTEREST e 60487.88
BILL121220R2HL 2 2 10001 INTEREST e 60487.88
BILL121220R2HL 2 3 10001 INTEREST f 526631.51
...
Then you can use that as a subquery (as an inline view or CTE) to form the basis for your grouping:
select reference_id as bill_reference, property, ref as repay_ref,
sum(amount) as repay_amount
from (
select reference_id, multivalue, subvalue, code,
first_value(property) over (partition by reference_id, multivalue) as property,
ref, amount
from your_table
)
group by reference_id, property, ref
order by reference_id, property, ref;
BILL_REFERENCE PROPERTY R REPAY_AMOUNT
-------------- -------- - ------------
BILL121220PBD8 BALANCE a 1061.08
BILL121220PBD8 BALANCE b 5395.89
BILL121220PBD8 BALANCE c 4043.07
BILL121220PBD8 BALANCE d 4100.22
BILL121220R2HL INTEREST e 120975.76
BILL121220R2HL INTEREST f 526631.51
...
(I retyped the reference IDs from your image to make up the test data, but couldn't be bothered to retype the individual ref's too. One of the reasons it's preferred to have text in questions rather than images.)
Are you trying to aggregate the amount by REF or REFERENCE_ID? If so, you should use group by clause.
For example, if you want to sum up the amount for each type of REF:
select REF, SUM(AMOUNT)
from BALANCE_TBL
group by REF

Trying to figure out top 5 land areas of the 50 states in the U.S

I have a table created. With one column named states and another column called land area. I am using oracle 11g. I have looked at various questions on here and cannot find a solution. Here is what I have tried so far:
SELECT LandAreas, State
FROM ( SELECT LandAreas, State, DENSE_RANK() OVER (ORDER BY State DESC) sal_dense_rank
FROM Map )
WHERE sal_dense_rank >= 5;
This does not provide the top 5 land areas as far as number wise.
I have also tried this one but no go either:
SELECT * FROM Map order by State desc)
where rownum < 5;
Anyone have any suggestions to get me on the right track??
Here is a samle of the table
states land areas
michagan 15000
florida 25000
tennessee 10000
alabama 80000
new york 150000
california 20000
oregon 5000
texas 6000
utah 3000
nebraska 1000
Desired output from query:
States land area
new york 150000
alabama 80000
florida 25000
california 20000
Try:
Select * from
(SELECT State, LandAreas FROM Map ORDER BY LandAreas DESC)
where rownum < 6
Link to Fiddle
Use a HAVING clause and count the number state states larger:
SELECT m.state, m.landArea
FROM Map m
LEFT JOIN Map m2 on m2.landArea > m.landArea
GROUP BY m.state, m.landArea
HAVING count(*) < 5
ORDER BY m.landArea DESC
See SQLFiddle
This joins each state to every state whose area is greater, then uses a HAVING clause to return only those states where the number of larger states was less than 5.
Ties are all returned, leading to more than 5 rows in the case of a tie for 5th.
The left join is needed for the case of the largest state, which has no other larger state to join to.
The ORDER BY is optional.
Try something like this
select m.states,m.landarea
from map m
where (select count(‘x’) from map m2 where m2.landarea > m.landarea)<=5
order by m.landarea
There are two bloomers in your posted code.
You need to use landarea in the DENSE_RANK() call. At the moment you're ordering the states in reverse alphabetical order.
Your filter in the outer query is the wrong way around: you're excluding the top four results.
Here is what you need ...
SELECT LandArea, State
FROM ( SELECT LandArea
, State
, DENSE_RANK() OVER (ORDER BY landarea DESC) as area_dr
FROM Maps )
WHERE area_dr <= 5
order by area_dr;
... and here is the SQL Fiddle to prove it. (I'm going with the statement in the question that you want the top 5 biggest states and ignoring the fact that your desired result set has only four rows. But adjust the outer filter as you will).
There are three different functions for deriving top-N result sets: DENSE_RANK, RANK and ROW_NUMBER.
Using ROW_NUMBER will always guarantee you 5 rows in the result set, but you may get the wrong result if there are several states with the same land area (unlikely in this case, but other data sets will produce such clashes). So: 1,2,3,4,5
The difference between RANK and DENSE_RANK is how they handle ties. DENSE_RANK always produces a series of consecutive numbers, regardless of how many rows there are in each rank. So: 1,2,2,3,3,3,4,5
RANK on the other hand will produce a sparse series if a given rank has more than one hit. So: 1,2,2,4,4,4.
Note that each of the example result sets has a different number of rows. Which one is correct? It depends on the precise question you want to ask.
Using a sorted sub-query with the ROWNUM pseudo-column will work like the ROW_NUMBER function, but I prefer using ROW_NUMBER because it is more powerful and more error-proof.

Resources