Discussions
Categories
- 196.9K All Categories
- 2.2K Data
- 240 Big Data Appliance
- 1.9K Data Science
- 450.4K Databases
- 221.7K General Database Discussions
- 3.8K Java and JavaScript in the Database
- 31 Multilingual Engine
- 550 MySQL Community Space
- 478 NoSQL Database
- 7.9K Oracle Database Express Edition (XE)
- 3K ORDS, SODA & JSON in the Database
- 546 SQLcl
- 4K SQL Developer Data Modeler
- 187.1K SQL & PL/SQL
- 21.3K SQL Developer
- 295.9K Development
- 17 Developer Projects
- 138 Programming Languages
- 292.6K Development Tools
- 107 DevOps
- 3.1K QA/Testing
- 646K Java
- 28 Java Learning Subscription
- 37K Database Connectivity
- 155 Java Community Process
- 105 Java 25
- 22.1K Java APIs
- 138.1K Java Development Tools
- 165.3K Java EE (Java Enterprise Edition)
- 18 Java Essentials
- 160 Java 8 Questions
- 86K Java Programming
- 80 Java Puzzle Ball
- 65.1K New To Java
- 1.7K Training / Learning / Certification
- 13.8K Java HotSpot Virtual Machine
- 94.3K Java SE
- 13.8K Java Security
- 204 Java User Groups
- 24 JavaScript - Nashorn
- Programs
- 443 LiveLabs
- 38 Workshops
- 10.2K Software
- 6.7K Berkeley DB Family
- 3.5K JHeadstart
- 5.7K Other Languages
- 2.3K Chinese
- 171 Deutsche Oracle Community
- 1.1K Español
- 1.9K Japanese
- 232 Portuguese
Query challenge
Comments
-
Hi Jens,
This is also an amazing solution, although as mentioned a bit cheated.
I started this thread thinking: "I know a bit of SQL and since I cannot come up with a non model clause solution I truly wonder if it is possible at all". And here I have two other solutions! You've got to love this forum ...
Thanks!
Regards,
Rob. -
You've got to love this forum ...I feel a XML solution was still missing
Let's keep this forum rock:SQL> SELECT VALUE(t).EXTRACT('ROW/CUSTOMER/text()').getnumberval() customer, 2 VALUE(t).EXTRACT('ROW/AMOUNT/text()').getnumberval() amount, 3 TO_DATE(VALUE(t).EXTRACT('ROW/STARTDATE/text()').getstringval(),'DD-MON-RR') startdate, 4 VALUE(t).EXTRACT('ROW/PERCENTAGE/text()').getnumberval() percentage, 5 ROUND(VALUE(t).EXTRACT('ROW/BALANCE_AT_END_OF_YEAR/text()').getnumberval(),2) balance_at_end_of_year 6 FROM XMLTABLE ('declare function local:baeoy($cust) 7 { 8 if($cust[1]) then 9 $cust[count($cust)]/PERCENTAGE * ($cust[count($cust)]/AMOUNT + local:baeoy($cust[position() <= count($cust) - 1])) 10 else (0) 11 }; (: end of baeoy :) 12 for $c in distinct-values(/ROWSET/ROW/CUSTOMER) 13 let $cust := /ROWSET/ROW[CUSTOMER = $c] 14 for $i at $row in $cust 15 return <ROW> 16 {$i/CUSTOMER} 17 {$i/AMOUNT} 18 {$i/STARTDATE} 19 {$i/PERCENTAGE} 20 <BALANCE_AT_END_OF_YEAR> {local:baeoy($cust[position() <= $row])} </BALANCE_AT_END_OF_YEAR> 21 </ROW>' PASSING XMLTYPE(CURSOR(SELECT customer, amount, startdate, (1 + percentage / 100) percentage, 0 balance_at_end_of_year 22 FROM deposits, interest_rates 23 WHERE startdate = deposit_date 24 ORDER BY customer, startdate))) t 25 / CUSTOMER AMOUNT STARTDATE PERCENTAGE BALANCE_AT_END_OF_YEAR ---------- ---------- --------- ---------- ---------------------- 1 1000 01-JAN-03 1.05 1050 1 200 01-JAN-04 1.032 1290 1 500 01-JAN-05 1.041 1863.39 1 100 01-JAN-06 1.058 2077.27 1 800 01-JAN-07 1.049 3018.25 2 20 01-JAN-03 1.05 21 2 150 01-JAN-04 1.032 176.47 2 60 01-JAN-05 1.041 246.17 2 100 01-JAN-06 1.058 366.25 2 100 01-JAN-07 1.049 489.09 10 rows selected. Regards, Michael
-
Let's keep this forum rock:It sure does this way: a third amazing solution!
Although I don't understand it (yet) and I get a ORA-00904 on my local 10.2.0.1.0 database:SQL> SELECT VALUE(t).EXTRACT('ROW/CUSTOMER/text()').getnumberval() customer,
Do you know why?
2 VALUE(t).EXTRACT('ROW/AMOUNT/text()').getnumberval() amount,
3 TO_DATE(VALUE(t).EXTRACT('ROW/STARTDATE/text()').getstringval(),'DD-MON-RR') startdate,
4 VALUE(t).EXTRACT('ROW/PERCENTAGE/text()').getnumberval() percentage,
5 ROUND(VALUE(t).EXTRACT('ROW/BALANCE_AT_END_OF_YEAR/text()').getnumberval(),2) balance_at_end_of_year
6 FROM XMLTABLE ('declare function local:baeoy($cust)
7 {
8 if($cust[1]) then
9 $cust[count($cust)]/PERCENTAGE * ($cust[count($cust)]/AMOUNT + local:baeoy($cust[position() <= count($cust)
- 1]))
10 else (0)
11 }; (: end of baeoy
12 for $c in distinct-values(/ROWSET/ROW/CUSTOMER)
13 let $cust := /ROWSET/ROW[CUSTOMER = $c]
14 for $i at $row in $cust
15 return <ROW>
16 {$i/CUSTOMER}
17 {$i/AMOUNT}
18 {$i/STARTDATE}
19 {$i/PERCENTAGE}
20 <BALANCE_AT_END_OF_YEAR> {local:baeoy($cust[position() <= $row])} </BALANCE_AT_END_OF_YEAR>
21 </ROW>' PASSING XMLTYPE(CURSOR(SELECT customer, amount, startdate, (1 + percentage / 100) percentage, 0
balance_at_end_of_year
22 FROM deposits, interest_rates
23 WHERE startdate = deposit_date
24 ORDER BY customer, startdate))) t
25 /
ROUND(VALUE(t).EXTRACT('ROW/BALANCE_AT_END_OF_YEAR/text()').getnumberval(),2) balance_at_end_of_year
*
FOUT in regel 5:
.ORA-00904: "T": ongeldige ID
As cd already suggested in another thread, I am also looking forward to some "Introduction on XML in SQL" written by you ... :-)
Regards,
Rob. -
.. and I get a ORA-00904 on my local 10.2.0.1.0 database:Pitty, hope I did not run in any bug here. My tested version is
SQL> select * from v$version; BANNER ---------------------------------------------------------------- Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Prod PL/SQL Release 10.2.0.3.0 - Production CORE 10.2.0.3.0 Production TNS for 32-bit Windows: Version 10.2.0.3.0 - Production NLSRTL Version 10.2.0.3.0 - Production
I've rewritten some part, since there was some issue with nls_numeric_characters as well. If you want, give this one a try too:michaels> ALTER SESSION SET nls_numeric_characters='.,' Session altered. michaels> SELECT EXTRACT (COLUMN_VALUE,'ROW/CUSTOMER/text()').getnumberval() customer, EXTRACT (COLUMN_VALUE,'ROW/AMOUNT/text()').getnumberval() amount, TO_DATE(EXTRACT (COLUMN_VALUE, 'ROW/STARTDATE/text()').getstringval (),'DD-MON-RR') startdate, EXTRACT (COLUMN_VALUE, 'ROW/PERCENTAGE/text()').getnumberval() percentage, ROUND(EXTRACT (COLUMN_VALUE, 'ROW/BALANCE_AT_END_OF_YEAR/text()').getnumberval(),2) balance_at_end_of_year FROM XMLTABLE ('declare function local:baeoy($cust) { if($cust[1]) then (0.01 * $cust[count($cust)]/PERCENTAGE + 1) * ($cust[count($cust)]/AMOUNT + local:baeoy($cust[position() <= count($cust) - 1])) else (0) }; (:-:) for $c in distinct-values(/ROWSET/ROW/CUSTOMER) let $cust := /ROWSET/ROW[CUSTOMER = $c] for $i at $row in $cust return <ROW> {$i/CUSTOMER} {$i/AMOUNT} {$i/STARTDATE} {$i/PERCENTAGE} <BALANCE_AT_END_OF_YEAR> {local:baeoy($cust[position() <= $row])} </BALANCE_AT_END_OF_YEAR> </ROW>' PASSING XMLTYPE(CURSOR(SELECT customer, amount, startdate, percentage, 0 balance_at_end_of_year FROM deposits, interest_rates WHERE startdate = deposit_date ORDER BY customer, startdate))) / CUSTOMER AMOUNT STARTDATE PERCENTAGE BALANCE_AT_END_OF_YEAR ---------- ---------- --------- ---------- ---------------------- 1 1000 01-JAN-03 5 1050 1 200 01-JAN-04 3,2 1290 1 500 01-JAN-05 4,1 1863,39 1 100 01-JAN-06 5,8 2077,27 1 800 01-JAN-07 4,9 3018,25 2 20 01-JAN-03 5 21 2 150 01-JAN-04 3,2 176,47 2 60 01-JAN-05 4,1 246,17 2 100 01-JAN-06 5,8 366,25 2 100 01-JAN-07 4,9 489,09 10 rows selected.
Regards,
Michael -
Michaels, I tested again.
First a:FOUT in regel 3:
And after setting my nls_date_format to DD-MON-RR a:
.ORA-01843: Geen geldige maand.FOUT in regel 1:
And most of all, this is s l o w . . . ., unbelievable for just 10 records.
.ORA-19112: fout bij evaluatie: oracle.xquery.XQException: XP0021: Dit is een dynamische fout die optreedt als een waarde in een
CAST-uitdrukking niet naar het vereiste type kan worden geconverteerd.
Detail: FORG0001: Ongeldige waarde voor conversie/constructor.
Regards,
Rob. -
Is this problem really different from "Hierarchical weighted total" -- hierarchy in this case collapsed to a linearly ordered sequence of dates? We have "part assembly"
grandchild(qty=1000) ----> child(qty=200) -----> parent(qty=500) ---->...
The links are weighed with interest rates, so that amended picture is:
grandchild(qty=1000) --1.05--> child(qty=200) ---1.032--> parent(qty=500) ---->...
The "Hierarchical weighted total" problem is to calculate aggregated qty at the root, and the "connect by" really shines for that kind of queries. Yet, the recursive with gives even more elegant answer.
================= Extract from "SQL Design patterns" ===============
Hierarchical Weighted Total
Before exploring aggregation on graphs, let’s have a quick look to aggregation on trees. Aggregation on trees is much simpler and has a designated name: hierarchical total. A typical query is
"Find combined salary of all the employees under (direct and indirect) supervision of King."
This query, however, has no inherent complexity, and effectively reduces to familiar task of finding a set all node’s descendants.
In graph context, the hierarchical total query is commonly referred to as bill of materials (BOM). Consider bicycle parts assembly
Part SubPart Quantity
Bicycle Wheel 2
Wheel Reflector 2
Bicycle Frame 1
Frame Reflector 2
Parts with the same name are assumed to be identical. They are modeled as nodes in an (acyclic directed) graph. The edges specify the construction flow:
1. Assemble each wheel from the required parts (including reflectors).
2. Assemble frame from the required components (including reflectors).
3. Assemble bicycle from the required parts (including frame and wheels).
Suppose we want to order the total list of parts for bike assembly. How do we calculate each part quantity? Unlike hierarchical total for a tree we can’t just add the quantities, as they multiply along each path.
Figure 6.11: Bicycle assembly graph. The total number of reflector parts should be calculated as the sum of parts aggregated along each path. The path /Bicycle/Wheel/Reflector contributes to 2*2=4 parts: bicycle has 2 wheels, each wheel has 2 reflectors. Likewise, the path /Bicycle/Frame/Reflector contribute to 1*2=2 more parts.
Therefore, there are two levels of aggregation here, multiplication of the quantities along each path, and summation along each alternative path.
----------------- Aggregation on Graphs (soapbox) -----------------
Two levels of aggregation fit naturally into in graphs queries. Consider finding a shortest path between two nodes. First, we add the distances along each path, and then we choose a path with minimal length.
Double aggregation is not something unique to graph queries, however. Consider
"Find the sum of the salaries grouped by department. Select maximum of them."
When expressing such a query in SQL, we accommodate the first sentence as an inner subquery inside the outer query corresponding to the second sentence
select max(salaryExpences) from (
select deptno, sum(sal) salaryExpenses
from Emp
group by dept
)
------------------------(end of soapbox)-------------------------
Hierarchical weighted total query has the same structure. The first level of aggregation where we join edges into paths is analogous to the group by subquery from the salary expense query. Formally, it is a transitive closure, which is enhanced with additional aggregates, or generalized transitive closure.
Figure 6.12: Generalized transitive closure. There are several aggregates naturally associated with each path: the first edge, the last edge, the path length, or any aggregate on the edge weights.
Let’s suggest some hypothetical SQL syntax for generalized transitive closure
select distinct first(Part), last(SubPart), product (Quantity)
from AssemblyEdges
connect by prior Part = later SubPart
Unfamiliar syntax requires clarification:
§ The product is a non-standard aggregate from chapter 4.
§ The first and last refer to the first and last edges in the path, correspondingly. These aggregates are unique to ordered structures such as (directed) graphs.
§ Although we didn’t use it in the example, concatenating edge labels into a string is one more natural aggregation function, which is unique to graphs. The list aggregate function is a standard way to accommodates it.
§ The later keyword is just a syntactic sugar fixing apparent asymmetry caused by the prior keyword.
§ There is no start with clause, which is, in fact, redundant. It is an outer query where we’ll restrict paths to those originated in the ‘Bicycle’ node.
The generalized transitive closure query is enveloped with second level of aggregation, which is accomplished by standard means
select leaf, sum(factoredQuantity) from (
select product(Quantity) factoredQuantity,
first(Part) root, last(SubPart) leaf
from AssemblyEdges
connect by prior Part = later SubPart
) where root = ‘Bicycle’
group by leaf
Enough theory, what do we do in real world to implement an aggregated weighted total query? Let’s start with Oracle, because the proposed syntax for generalized transitive closure resembles Oracle connect by.
First, we have to be able to refer to the first and the last edges in the path
select connect_by_root(Part) root, SubPart leaf
from AssemblyEdges
connect by prior Part = SubPart
Unlike our fictional syntax, Oracle treats edges in the path asymmetrically. Any column from the AssemblyEdges table is assumed to (implicitly) refer to the last edge. This design dates back to version 7. The connect_by_root function referring to the first edge has been added in version 10. It is remarkable how this, essentially ad-hock design proved to be successful in practice.
Next, Oracle syntax has several more path aggregate functions:
1. level – the length of the path.
2. sys_connect_by_path – which is essentially the list aggregate in our fictitious syntax.
3. connect_by_is_leaf – an indicator if there is no paths which contain the current path as a prefix.
4. connect_by_is_cycle – an indicator if the first edge is adjacent to the last.
Unfortunately, we need an aggregate which is a product of edge weights, and not a string which is a concatenation of weights produced by sys_connect_by_path. You might be tempted to hack a function which accepts a string of the edge weights, parses it, and returns the required aggregate value.
Nah, too easy! Can we solve the problem without coding a function (even a simple one)? Yes we can, although this solution would hardly be more efficient. The critical idea is representing any path in the graph as a concatenation of three paths:
§ a prefix path from the start node to some intermediate node i
§ a path consisting of a single weighted edge (i,j)
§ a postfix path from the node i to the end node
Figure 6.13: A path from node x to node y is a composition of the path from node x to node i, the edge (i, j), and the path from j to y. The edge (i, j) can be positioned anywhere along the path from x to y.
Then, we freeze the path from x to y, while interpreting the edge (i,j) as a variable. All we need to do is aggregating the weights of all those edges.
Let’s assemble the solution piece by piece. First, all the paths in the graphs are expressed as
with TClosure as (
select distinct connect_by_root(Part) x, SubPart y,
sys_connect_by_path('['||Part||','||SubPart||'>',' ') path
from AssemblyEdges
connect by nocycle Part=prior SubPart
union
select Part, Part, '' from AssemblyEdges
union
select SubPart, SubPart, '' from AssemblyEdges
) …
This is essentially reflexive transitive closure relation enhanced with the path column. Paths are strings of concatenated edges; each edge is sugarcoated into '['||Part||','||SubPart||'>', which helps visual perception, but is inessential for the solution.
Next, we join two paths and intermediate edge together, and group by paths
… , PathQuantities as (
select t1.x, t2.y,
t1.p||' ['||e.Part||','||e.SubPart||'>'||t2.p,
product(Quantity) Quantity
from TClosure t1, AssemblyEdges e, TClosure t2
where t1.y = e.Part and e.SubPart = t2.x
group by t1.x, t2.y, t1.p||' ['||e.Part||','||e.SubPart||'>'||t2.p
) …
There we have all the paths with quantities aggregated along them. Let’s group the paths by the first and last node in the path while adding the quantities
select x, y, sum(Quantity)
from PathQuantities
group by x, y
This query is almost final, as it needs only a minor touch: restricting the node x to ‘Bicycle’ and interpreting the y column as a Part in the assembly
select y Part, sum(Quantity)
from PathQuantities
where x = ‘Bicycle’
group by x, y
Let’s move on to recursive SQL solution. It turns out to be quite satisfactory
with TCAssembly as (
select Part, SubPart, Quantity AS factoredQuantity
from AssemblyEdges
where Part = ‘Bicycle’
union all
select te.Part, e.SubPart, e.Quantity * te.factoredQuantity
from TCAssembly te, AssemblyEdges e
where te.SubPart = e.Part
) select SubPart, sum(Quantity) from TCAssembly
group by SubPart
The most important, it accommodated the inner aggregation with non-standard aggregate effortlessly! Second, the cycle detection issue that plagued recursive SQL in the section on transitive closure is not a problem for directed acyclic graphs. -
Rob van Wijk wrote:It could be done without any cheating.
Hi Jens,
This is also an amazing solution, although as mentioned a bit cheated.create or replace type two as object (x number, y number); / create or replace type mul_type as object ( total number, static function ODCIAggregateInitialize(sctx IN OUT mul_type) return number, member function ODCIAggregateIterate(self IN OUT mul_type, value IN two) return number, member function ODCIAggregateTerminate(self IN mul_type, returnValue OUT varchar2, flags IN number) return number, member function ODCIAggregateMerge(self IN OUT mul_type, ctx2 IN mul_type) return number ); / create or replace type body mul_type is static function ODCIAggregateInitialize(sctx IN OUT mul_type) return number is begin sctx := mul_type(0); return ODCIConst.Success; end; member function ODCIAggregateIterate(self IN OUT mul_type, value IN two) return number is begin self.total := round((self.total + value.x) * value.y, 2); return ODCIConst.Success; end; member function ODCIAggregateTerminate(self IN mul_type, returnValue OUT varchar2, flags IN number) return number is begin returnValue := self.total; return ODCIConst.Success; end; member function ODCIAggregateMerge(self IN OUT mul_type, ctx2 IN mul_type) return number is begin return ODCIConst.Success; end; end; / CREATE OR REPLACE FUNCTION mulagg(input two) RETURN number AGGREGATE USING mul_type; /
SQL> select d.*, 2 i.*, 3 mulagg(two(d.amount, 1 + i.percentage / 100)) over(partition by d.customer order by d.deposit_date) balance_at_end_of_year 4 from deposits d, interest_rates i 5 where d.deposit_date = i.startdate 6 / CUSTOMER AMOUNT DEPOSIT_DATE STARTDATE PERCENTAGE BALANCE_AT_END_OF_YEAR ---------- ---------- ------------ ----------- ---------- ---------------------- 1 1000 01.01.2003 01.01.2003 5 1050 1 200 01.01.2004 01.01.2004 3,2 1290 1 500 01.01.2005 01.01.2005 4,1 1863,39 1 100 01.01.2006 01.01.2006 5,8 2077,27 1 800 01.01.2007 01.01.2007 4,9 3018,26 2 20 01.01.2003 01.01.2003 5 21 2 150 01.01.2004 01.01.2004 3,2 176,47 2 60 01.01.2005 01.01.2005 4,1 246,17 2 100 01.01.2006 01.01.2006 5,8 366,25 2 100 01.01.2007 01.01.2007 4,9 489,1 10 rows selected
-
Another one solution. Just for fun. (self join makes this solution unattractive)
create or replace type xyz as object (x number, y number, z number) / create or replace type xyz_tbl as table of xyz / create or replace function mul(p in xyz_tbl) return number is Result number := 0; begin for i in (select x, y from table(p) order by z) loop result := round((result + i.x) * i.y, 2); end loop; return result; end mul; /
SQL> select d.*, 2 i.*, 3 mul(cast(multiset(select xyz(d1.amount, 4 1 + i1.percentage / 100, 5 row_number() over(order by i1.startdate)) 6 from deposits d1, interest_rates i1 7 where i1.startdate = d1.deposit_date 8 and d1.deposit_date <= d.deposit_date 9 and d1.customer = d.customer) as xyz_tbl)) balance_at_end_of_year 10 from deposits d, interest_rates i 11 where d.deposit_date = i.startdate 12 / CUSTOMER AMOUNT DEPOSIT_DATE STARTDATE PERCENTAGE BALANCE_AT_END_OF_YEAR ---------- ---------- ------------ ----------- ---------- ---------------------- 1 1000 01.01.2003 01.01.2003 5 1050 1 200 01.01.2004 01.01.2004 3,2 1290 1 500 01.01.2005 01.01.2005 4,1 1863,39 1 100 01.01.2006 01.01.2006 5,8 2077,27 1 800 01.01.2007 01.01.2007 4,9 3018,26 2 20 01.01.2003 01.01.2003 5 21 2 150 01.01.2004 01.01.2004 3,2 176,47 2 60 01.01.2005 01.01.2005 4,1 246,17 2 100 01.01.2006 01.01.2006 5,8 366,25 2 100 01.01.2007 01.01.2007 4,9 489,1 10 rows selected SQL> select customer, 2 amount, 3 deposit_date, 4 startdate, 5 percentage, 6 mul(cast(collect(tmp) as xyz_tbl)) balance_at_end_of_year 7 from (select d.*, 8 i.*, 9 xyz(d1.amount, 10 1 + i1.percentage / 100, 11 row_number() over(order by i1.startdate)) tmp 12 from deposits d, interest_rates i, deposits d1, interest_rates i1 13 where d.deposit_date = i.startdate 14 and i1.startdate = d1.deposit_date 15 and d1.deposit_date <= d.deposit_date 16 and d1.customer = d.customer) 17 group by customer, amount, deposit_date, startdate, percentage 18 order by customer, startdate 19 / CUSTOMER AMOUNT DEPOSIT_DATE STARTDATE PERCENTAGE BALANCE_AT_END_OF_YEAR ---------- ---------- ------------ ----------- ---------- ---------------------- 1 1000 01.01.2003 01.01.2003 5 1050 1 200 01.01.2004 01.01.2004 3,2 1290 1 500 01.01.2005 01.01.2005 4,1 1863,39 1 100 01.01.2006 01.01.2006 5,8 2077,27 1 800 01.01.2007 01.01.2007 4,9 3018,26 2 20 01.01.2003 01.01.2003 5 21 2 150 01.01.2004 01.01.2004 3,2 176,47 2 60 01.01.2005 01.01.2005 4,1 246,17 2 100 01.01.2006 01.01.2006 5,8 366,25 2 100 01.01.2007 01.01.2007 4,9 489,1 10 rows selected
-
Hi,4 years old members :-)
Hehehe,I like recursive with clause B-)with tmp as( select a.customer,a.amount,a.deposit_date,b.percentage, Row_Number() over(partition by customer order by a.deposit_date) as rn from deposits a,interest_rates b where a.deposit_date=b.startdate), rec(customer,amount,deposit_date,PERCENTAGE,rn,BALANCE) as( select customer,amount,deposit_date,percentage,rn,amount*(1+percentage/100) from tmp a where rn=1 union all select b.customer,b.amount,b.deposit_date,b.percentage,b.rn, (a.BALANCE+b.amount)*(1+b.percentage/100) from rec a,tmp b where a.rn+1=b.rn and a.customer = b.customer) select*from rec order by customer,deposit_date; customer amount deposit_ percentage rn balance -------- ------ -------- ---------- -- --------- 1 1000 03-01-01 5 1 1050 1 200 04-01-01 3.2 2 1290 1 500 05-01-01 4.1 3 1863.39 1 100 06-01-01 5.8 4 2077.2666 1 800 07-01-01 4.9 5 3018.2527 2 20 03-01-01 5 1 21 2 150 04-01-01 3.2 2 176.472 2 60 05-01-01 4.1 3 246.16735 2 100 06-01-01 5.8 4 366.24506 2 100 07-01-01 4.9 5 489.09107
This discussion has been closed.