Oracle Query - to sum up the balances on some criteria - oracle

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

Related

Oracle: Update values in table with aggregated values from same table

I am looking for a possibly better approach to this.
I have created a temp table in Oracle 11.2 that I'm using to pre calculate values that I will need in other selects instead of always generating them again with each select.
create global temporary table temp_foo (
DT timestamp(6), --only the date part will be used in this example but for later things I will need the time
Something varchar2(100),
Customer varchar2(100),
MinDate timestamp(6),
MaxDate timestamp(6),
Filecount int,
Errorcount int,
AvgFilecount int,
constraint PK_foo primary key (DT, Customer)
) on commit preserve rows;
I then first insert some fixed values for everything except AvgFilecount. AvgFilecount should contain the average for the Filecount for the 3 previous records (going by the date in DT). It doesn’t matter that the result will be converted to an int, I don’t need the decimal places
DT | Customer | Filecount | AvgFilecount
2019-04-30 | x | 10 | avg(2+3+9)
2019-04-29 | x | 2 | based on values before this
2019-04-28 | x | 3 | based on values before this
2019-04-27 | x | 9 | based on values before this
I thought about using a normal UPDATE statement as this should be faster than looping through the values. I should mention that there are no gaps in the DT field but obviously there is a first one where I won‘t find any previous records. If I would loop through, I could easily calculate AvgFilecount with (the record before previous record/2 + previous record)/3 which I cannot with UPDATE as I cannot guarantee the order of how they are executed. So I‘m fine with just taking the last 3 records (going by DT) and calcuting it from there.
What I thought would be an easy update is giving me headaches. I‘m mostly doing SQL Server where I would just join the 3 other records but it seems is a bit different in Oracle. I have found https://stackoverflow.com/a/2446834/4040068 and wanted to use the second approach in the answer.
update
(select curr.DT, curr.temp_foo, curr.Filecount, curr.AvgFilecount as OLD, (coalesce(Minus1.Filecount, 0) + coalesce(Minus2.Filecount, 0) + coalesce(Minus3.Filecount, 0)) / 3 as NEW
from temp_foo curr
left join temp_foo Minus1 ON Minus1.Customer = curr.Customer and trunc(Minus1.DT) = trunc(curr.DT-1)
left join temp_foo Minus2 ON Minus2.Customer = curr.Customer and trunc(Minus2.DT) = trunc(curr.DT-2)
left join temp_foo Minus3 ON Minus3.Customer = curr.Customer and trunc(Minus3.DT) = curr.DT-3
order by 1, 2
)
set OLD = NEW;
Which gives me an
ORA-01779: cannot modify a column which maps to a non key-preserved
table
01779. 00000 - "cannot modify a column which maps to a non key-preserved table"
*Cause: An attempt was made to insert or update columns of a join view which
map to a non-key-preserved table.
*Action: Modify the underlying base tables directly.
I thought this should work as both join conditions are in the primary key and thus unique. I am currently implementing the first approach in the above mentioned answer but it is getting quite big and it feels like there should be a better solution to this.
Other things I thought about trying:
using a nested subselect (nested because Oracle doesn’t know top(n) and I need to sort the subselect) to select the previous 3 records ordered by DT and then he outer select with rownum <=3 and then I could just use AVG(). However, I was told subselect can be quite slow and joins are better in Oracle performance wise. Dunno if that is really the case, haven‘t done any testing
Edit: My insert right now looks like this. I am already aggregating the Filecount for a day as there can be multiple records per DT per Customer per Something.
insert into temp_foo (DT, Something, Customer, Filecount)
select dates.DT, tbl1.Something, tbl1.Customer, coalesce(sum(tbl3.Filecount),0)
from table(Function_Returning_Daterange(NULL, NULL)) dates
cross join
(SELECT Something,
Code,
Value
FROM Table2 tbl2
WHERE (Something = 'Value')) tbl1
left outer join Table3 tbl3
on tbl3.Customer = tbl1.Customer
and trunc(tbl3.MinDate) = trunc(dates.DT)
group by dates.DT, tbl1.Something, tbl1.Customer;
You could use an analytic average with a window clause:
select dt, customer, filecount,
avg(filecount) over (partition by customer order by dt
rows between 3 preceding and 1 preceding) as avgfilecount
from tmp_foo
order by dt desc;
DT CUSTOMER FILECOUNT AVGFILECOUNT
---------- -------- ---------- ------------
2019-04-30 x 10 4.66666667
2019-04-29 x 2 6
2019-04-28 x 3 9
2019-04-27 x 9
and then do the update part with a merge statement:
merge into tmp_foo t
using (
select dt, customer,
avg(filecount) over (partition by customer order by dt
rows between 3 preceding and 1 preceding) as avgfilecount
from tmp_foo
) s
on (s.dt = t.dt and s.customer = t.customer)
when matched then update set t.avgfilecount = s.avgfilecount;
4 rows merged.
select dt, customer, filecount, avgfilecount
from tmp_foo
order by dt desc;
DT CUSTOMER FILECOUNT AVGFILECOUNT
---------- -------- ---------- ------------
2019-04-30 x 10 4.66666667
2019-04-29 x 2 6
2019-04-28 x 3 9
2019-04-27 x 9
You haven't shown your original insert statement; it might be possible to add the analytic calculation to that, and avoid the separate update step.
Also, if you want the first two date values to be calculated as if the 'missing' extra days before them had zero counts, you could use sum and division instead of avg:
select dt, customer, filecount,
sum(filecount) over (partition by customer order by dt
rows between 3 preceding and 1 preceding)/3 as avgfilecount
from tmp_foo
order by dt desc;
DT CUSTOMER FILECOUNT AVGFILECOUNT
---------- -------- ---------- ------------
2019-04-30 x 10 4.66666667
2019-04-29 x 2 4
2019-04-28 x 3 3
2019-04-27 x 9
It depends what you expect those last calculated values to be.

SQL sample groups

I have a sqlite database that I can read as:
In [42]: df = pd.read_sql("SELECT * FROM all_vs_all", engine)
In [43]:
In [43]: df.head()
Out[43]:
user_data user_model \
0 037d05edbbf8ebaf0eca#172.16.199.165 037d05edbbf8ebaf0eca#172.16.199.165
1 037d05edbbf8ebaf0eca#172.16.199.165 060210bf327a3e3b4621#172.16.199.33
2 037d05edbbf8ebaf0eca#172.16.199.165 1141259bd36ba65bef02#172.21.44.180
3 037d05edbbf8ebaf0eca#172.16.199.165 209627747e2af1f6389e#172.16.199.181
4 037d05edbbf8ebaf0eca#172.16.199.165 303a1aff4ab6e3be82ab#172.21.112.182
score Class time_secs model_name bin_id
0 0.283141 0 1514764800 Flow 0
1 0.999300 1 1514764800 Flow 0
2 1.000000 1 1514764800 Flow 0
3 0.206360 1 1514764800 Flow 0
4 1.000000 1 1514764800 Flow 0
As the table is too big I rather than reading the full table I select a random subset of rows:
This can be done very quckly as:
random_query = "SELECT * FROM all_vs_all WHERE abs(CAST(random() AS REAL))/9223372036854775808 < %f AND %s" % (ratio, time_condition)
df = pd.read_sql(random_query, engine)
The problem is that for each triplet [user_data, user_model, time_secs] I want to get all the rows containing that triplet. Each triplet appears 1 or 2 times.
A possible way to do it is to firstly sample a random set of triplets and then get all the rows that have one of the selected triplets but this seems to be too slow.
Is there an efficient way to do it?
EDIT: If I could load all the data in pandas I would have done something like:
selected_groups = []
for group in df.groupby(['user_data', 'user_model', 'time_secs']):
if np.random.uniform(0,1) > ratio:
selected_groups.append(group)
res = pd.concat(selected_groups)
Few sample join and sql query:
currently admitted :
Select p.patient_no, p.pat_name,p.date_admitted,r.room_extension,p.date_discharged FROM Patient p JOIN room r ON p.room_location = r.room_location where p.date_discharged IS NULL ORDER BY p.patient_no,p.pat_name,p.date_admitted,r.room_extension,p.date_discharged;
vacant rooms:
SELECT r.room_location, r.room_accomadation, r.room_extension FROM room r where r.room_location NOT IN (Select p.room_location FROM patient.p where p.date_discharged IS NULL) ORDER BY r.room_location, r.room_accomadation, r.room_extension;
no charges yet :
SELECT p.patient_no, p.pat_name, COALESCE (b.charge,0.00) charge FROM patient p LEFT JOIN billed b on p.patient_no = b.patient_no WHERE p.patient_no NOT IN (SELECT patient_no FROM billed) group by p.patient_no ORDER BY p.patient_no, p.pat_name,charge;
max salarised :
SELECT phy_id,phy_name, salary FROM physician where salary in (SELECT MAX(salary) FROM physician) UNION
SELECT phy_id,phy_name, salary FROM physician where salary in (SELECT MIN(salary) FROM physician) ORDER BY phy_id,phy_name, salary;
various item consumed by:
select p.pat_name, i.discription, count (i.item code) as item code from patient p join billed b on p.patient no = b. patient no join item i on b.item code = i.item code group by p.patient no, i.item code order by..
patient not receivede treatment:
SELECT p.patient_no,p.pat_name FROM patient p where p.patient_no NOT IN (SELECT t.patient_no FROM treats t)
ORDER BY p.patient_no,p.pat_name;
2 high paid :
Select phy_id, phy_name, date_of_joining, max(salary) as salary from physician group by salary having salary IN (Select salary from physician)
Order by phy_id, phy_name, date_of_joining, salary limit 2;
over 200:
select patient_no, sum (charge), as total charge from billed group by patient no having total charges > 200 order by patient no, total charges

DAX AVG of Group Applied but returned for one specific Employee?

I have data as follows
EmployeeID Cycle Val Group
1 1 6 A
2 1 5 A
My desired result is as follows:
EmployeeID Cycle GroupVal
1 1 5.5
2 1 5.5
I have written 2 Measures as follows:
Emp_AVG: CALCULATE(AVERAGE(EmployeeFeedback, EmployeeFeedback[Val] > 0)
Group_AVG: CALCULATE(AVERAGEX(EmployeeFeedback[Emp_AVG],EmployeeFeedback[Emp_AVG] >0)
My thought process is that the Group_AVG is averaging the avg of all employees PER GROUP however since i need the results for a SPECIFIC employee, as soon as i introduce that column, it starts slicing by the Employee and the Group avg becomes inaccurate. I guess i need to generate Group Avgs before i do any employee filtering..how?
I am running a DAX query as follows:
EVALUATE SUMMARIZECOLUMNS(
EmployeeFeedback[EmployeeID],
EmployeeFeedback[Cycle],
"Group Val", [Group_AVG]
)
I need the EmployeeID to filter it down to an employee but because of EmployeeID, the Group AVG gets screwed. Without EmployeeID, Group AVG is correct but then there is no way to filter it for a specific Employee!
Thanks!
You could try to provide the ALLEXCEPT() argument to the CALCULATE function.
Btw, I believe there's an error in your Emp_AVG measure.
Try this:
Employee Average
Emp_AVG =
CALCULATE(
AVERAGE(EmployeeFeedback[Val]),
EmployeeFeedback[Val] > 0)
Group Average
Grp_Avg =
CALCULATE(EmployeeFeedback[Emp_AVG],
ALLEXCEPT(EmployeeFeedback,EmployeeFeedback[Group]))
Result:

SUM with distinct multiple lines

DB: ORACLE
Hi guys. I am constructing a query and I have the follow situation:
My table
---------------------------------------
Risk Risk Factor Control
---------------------------------------
RK 1 RF 1 Control 1
RK 1 RF 1 Control 2
RK 2 RF 3 Control 1
---------------------------------------
So I'd like to sum how much Risks Factors I have per risks e how much controls I have per Risk too.
Result
--------------------------------------
Risk SUM RF SUM Control
--------------------------------------
RK 1 1 2
RK 2 1 1
--------------------------------------
Does anyone knows how to fix this problem?
Kind Regards
I tried a simple sum. I created a view when a have the relation between Risk Factor and Control so I made a join with risk table, example:
SELECT RK.NAME,
SUM(CASE WHEN RFC.RISKFACTOR IS NOT NULL THEN 1 ELSE 0) SUM_RK,
SUM(CASE WHEN RFC.CONTROL IS NOT NULL THEN 1 ELSE 0) SUM_CONTROL
FROM T_RISK RK
JOIN V_RF_CONTROL RFC
ON RFC.RELATIONID = RK.RISKID
You don't need to sum here - you just need to count the distinct values:
SELECT RK.NAME,
COUNT(DISTINCT RFC.RISKFACTOR) SUM_RK,
COUNT(DISTINCT RFC.CONTROL) SUM_CONTROL
FROM T_RISK RK
JOIN V_RF_CONTROL RFC ON RFC.RELATIONID = RK.RISKID

Select Multiple rows into one row in 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;

Resources