Dividing a sum value into multiple rows due to field length constraint - sql

I am migrating financial data from a very large table (100 million+ of rows) by summarizing the amount and insert them into summary table. I ran into problem when the summary amount (3 billions) is larger than what the field in the summary table can hold (can only hold up to 999 millions.) Changing the field size is not an option as it requires a change process.
The only option I have is to divide the amount (the one that breach the size limit) into smaller ones so it can be inserted into the table.
I came across this SQL - I need to divide a total value into multiple rows in another table which is similar except the number of rows I need to insert is dynamic.
For simplicity, this is how the source table might look like
account_table
acct_num | amt
-------------------------------
101 125.00
101 550.00
101 650.00
101 375.00
101 475.00
102 15.00
103 325.00
103 875.00
104 200.00
104 275.00
The summary records are as follows
select acct_num, sum(amt)
from account_table
group by acct_num
Account Summary
acct_num | amt
-------------------------------
101 2175.00
102 15.00
103 1200.00
104 475.00
Assuming the maximum value in the destination table is 1000.00, the expected output will be
summary_table
acct_num | amt
-------------------------------
101 1000.00
101 1000.00
101 175.00
102 15.00
103 1000.00
103 200.00
104 475.00
How do I create a query to get the expected result? Thanks in advance.

You need a numbers table. If you have a handful of values, you can define it manually. Otherwise, you might have one on hand or use a similar logic:
with n as (
select (rownum - 1) as n
from account_table
where rownum <= 10
),
a as (
select acct_num, sum(amt) as amt
from account_table
group by acct_num
)
select acct_num,
(case when (n.n + 1) * 1000 < amt then 1000
else amt - n.n * 1000
end) as amt
from a join
n
on n.n * 1000 < amt ;

A variation along these lines might give some ideas (using the 1,000 of your sample data):
WITH summary AS (
SELECT acct_num
,TRUNC(SUM(amt) / 1000) AS times
,MOD(SUM(amt), 1000) AS remainder
FROM account_table
GROUP BY acct_num
), x(acct_num, times, remainder) AS (
SELECT acct_num, times, remainder
FROM summary
UNION ALL
SELECT s.acct_num, x.times - 1, s.remainder
FROM summary s
,x
WHERE s.acct_num = x.acct_num
AND x.times > 0
)
SELECT acct_num
,CASE WHEN times = 0 THEN remainder ELSE 1000 END AS amt
FROM x
ORDER BY acct_num, amt DESC
The idea is to first build a summary table with div and modulo:
ACCT_NUM TIMES REMAINDER
101 2 175
102 0 15
103 1 200
104 0 475
Then perform a hierarchical query on the summary table based on the number of "times" (i.e. rows) you want, with an extra for the remainder.
ACCT_NUM AMT
101 1000
101 1000
101 175
102 15
103 1000
103 200
104 475

Related

Cumulative vs. Tiered Calculation in SQL Query or Inline Table Value Function with Conditional Logic?

I'm working with two tables of data, but different methodologies to derive two distinct desired outputs below.
The first is a cumulative tier calculation and the second is a just a tiered lookup based on a range. I need to be able to return this calculation for a row item transaction based on some JOINS using foreign keys to other dimension tables, namely Accounts and Regions and Tier Type. For example, a single account could have a calculation be either cumulative or tiered based on the region's and/or account's ID.
Link to DB Fiddle
Type ID
Name
1
Cumulative
2
Tiered
Tiered table:
Account ID
Type ID
Region ID
TierNo
Min
Max
Total A
Total B
101
1
2
1
0
10000
.90
.10
101
1
2
2
10001
30000
.60
.40
101
1
2
3
30001
100000
.40
.60
101
1
2
4
100001
500000
.40
.60
101
1
2
5
500001
999999999999
.20
.80
102
1
3
1
0
7800
.80
.20
102
1
3
2
7801
12800
.70
.30
102
1
3
3
12801
34000
.60
.40
102
1
3
4
34001
50000
.50
.50
102
1
3
5
5000 1
999999999999
.50
.50
103
2
1
1
0
10000
.90
.10
103
2
1
2
10001
30000
.60
.40
103
2
1
3
30001
100000
.40
.60
103
2
1
4
100001
500000
.40
.60
103
2
1
5
500001
999999999999
.20
.80
Current table sample:
Trans ID
Account ID
Type ID
Region ID
GrossAmt
Total A %
Total A $
Net Amt
100001
101
1
2
42650
100002
102
1
3
42650
100003
103
2
1
42650
Desired output:
Trans ID
Account ID
Type ID
Region ID
GrossAmt
Total A %
Total A $
Net Amt
100001
101
1
2
42650
0.611
26059.99
16589.99
100002
102
1
3
42650
0.628
26784.98
15864.99
100003
103
2
1
42650
0.40
17060.00
25590.00
I've been able to make some edits to a previous post to get the accounts, but can't seem to figure out the logic for the tiered lookup value in TransID 100003.
Ideally, I'd prefer to create this logic in a table value function (or two) and then incorporate it into a View that I'll use for reporting in a web form using C#.
SELECT
c.*,
[Total A %] = t.Total / c.GrossAmt,
[Total A $] = t.Total,
[Net Amt] = c.GrossAmt - t.Total
FROM #temp c
INNER JOIN Accounts a ON a.[Account ID] = c.[Account ID]
CROSS APPLY (
SELECT
Total = SUM((v.ActualMax - t.[Min]) * t.[Total A %])
FROM [dbo].[Tiered Table] t
CROSS APPLY (VALUES(
CASE WHEN c.GrossAmt < t.[Max] THEN c.GrossAmt ELSE t.[Max] END
)) v(ActualMax)
WHERE c.GrossAmt > t.[Min] AND t.[Account ID] = c.[Account ID]
) t;
Any ideas or guidance would be extremely helpful and appreciated.
It seems to be a simple matter of AND OR logic. You need to exclude rows which are TypeID = 2 and also have their maximum below the level of GrossAmt.
Then you just conditionally aggregate either the total amount (for tiered rows only, there will be only one row) or just the amount for that tier (for cumulative tiers).
SELECT
c.*,
[Total A %] = t.Total / c.GrossAmt,
[Total A $] = t.Total,
[Net Amt] = c.GrossAmt - t.Total
FROM CurrentData c
INNER JOIN Accounts a ON a.[AccountID] = c.[AccountID]
CROSS APPLY (
SELECT
Total = SUM(CASE WHEN t.TypeID = 2 THEN v.GrossAmt ELSE (v.ActualMax - t.[Min]) END * t.[Total A])
FROM [dbo].[Tiers] t
CROSS APPLY (VALUES(
CASE WHEN c.GrossAmt < t.[Max] THEN c.GrossAmt ELSE t.[Max] END,
c.GrossAmt
)) v(ActualMax, GrossAmt)
WHERE t.[AccountID] = c.[AccountID]
AND t.TypeID = c.TypeID
AND t.RegionID = c.RegionID
AND c.GrossAmt > t.[Min]
AND (t.TypeID = 1 OR c.GrossAmt <= t.Max)
) t;
db<>fiddle
The second CROSS APPLY is only necessary because of aggregating outer values. You don't need this if you place it in a function, as shown in your previous question.
Note that you should use half-open intervals here. In other words, either Min or Max should be exclusive. Otherwise there may be values that can "fall through the cracks".

SQL update statement to sum column in one table, then add the total to a different column/table

Evening all, hoping for some pointers with an SQL Server query if possible.
I have two tables in a database, example as follows:
PostedTran
PostedTranID AccountID PeriodID Value TransactionDate
1 100 120 100 2019-01-01
2 100 120 200 2020-01-01
3 100 130 300 2021-01-01
4 101 120 400 2020-01-01
5 101 130 500 2021-01-01
PeriodValue
PeriodValueID AccountID PeriodID ActualValue
10 100 120 500
11 101 120 600
I have a mismatch in the two tables, and I'm failing miserably in my attempts. From the PostedTran table, I'm trying to select all transaction lines dated before 2021-01-01, then sum the Value for each AccountID from the results. I then need to add that value to the existing ActualValue in the PeriodValue table.
So, in the above example, the ActualValue on PeriodValueID 10 will update to 800, and 11 to 1000. The PeriodID in this example is constant and will always be 120.
Thanks in advance for any help.
Since RDMS not mentioned, pseudo-sql looks like:
with DataSum as
(
select AccountID, PeriodID, sum(Value) as TotalValue
from PostedTran
where TransactionDate<'1/1/2021'
group by AccountID, PeriodID
)
update PeriodValue set ActualValue = ActualValue + ds.TotalVaue
from PeriodValue pv inner join DataSum ds
on pv.accountid=ds.accountid and pv.periodid=ds.periodid
The following should do what you ask. I haven't included PeriodId in the correlation as you did not specify it in your description, however you can just include it if it's required.
update pv set pv.ActualValue=pv.ActualValue + t.Value
from PeriodValue pv
cross apply (
select Sum(value) value
from PostedTran pt
where pt.AccountId=pv.AccountId and pt.TransactionDate <'20210101'
)t

SQL DB2 Toad - Sum from two tables by ID

I was hoping to find the sum from two tables with columns ID and Amount, grouping by ID.
My first attempt was to UNION the two tables first and then conduct a sum and group by, but I was hoping to know of a better way.
Inputs:
Table 1
ID Amount
123 100
123 100
145 500
167 600
Table 2
ID Amount
123 100
123 100
145 500
199 600
Output
ID Amount
123 400
145 1000
167 600
199 600
You can do:
select id, sum(amount) as amount
from (
select id, amount from table_1
union all
select id, amount from table_2
) x
group by id

SQL Query to continuously bucket data

I have a table as follows:
Datetime | ID | Price | Quantity
2013-01-01 13:30:00 1 139 25
2013-01-01 13:30:15 2 140 25
2013-01-01 13:30:30 3 141 15
Supposing that I wish to end up with a table like this, which buckets the data into quantities of 50 as follows:
Bucket_ID | Max | Min | Avg |
1 140 139 139.5
2 141 141 141
Is there a simple query to do this? Data will constantly be added to the first table, it would be nice if it could somehow not recalculate the completed buckets of 50 and instead automatically start averaging the next incomplete bucket. Ideas appreciated! Thanks
You may try this solution. It should work even if "number" is bigger than 50 (but relying on fact that avg(number) < 50).
select
bucket_id,
max(price),
min(price),
avg(price)
from
(
select
price,
bucket_id,
(select sum(t2.number) from test t2 where t2.id <= t1.id ) as accumulated
from test t1
join
(select
rowid as bucket_id,
50 * rowid as bucket
from test) buckets on (buckets.bucket - 50) < accumulated
and buckets.bucket > (accumulated - number))
group by
bucket_id;
You can have a look at this fiddle http://sqlfiddle.com/#!7/4c63c/1 if it is what you want.

oracle sql query to get data from two tables of similar type

I have two tables ACTUAL AND ESTIMATE having unique column(sal_id, gal_id, amount, tax).
In ACTUAL table I have
actual_id, sal_id, gal_id, process_flag, amount, tax
1 111 222 N 100 1
2 110 223 N 200 2
In ESTIMATE table I have
estimate_id, sal_id, gal_id, process_flag, amount, tax
3 111 222 N 50 1
4 123 250 N 150 2
5 212 312 Y 10 1
Now I want a final table, which should have record from ACTUAL table and if no record exist for sal_id+gal_id mapping in ACTUAL but exist in ESTIMATE, then populate estimate record (along with addition of amount and tax).
In FINAL table
id sal_id, gal_id, actual_id, estimate_id, total
1 111 222 1 null 101 (since record exist in actual table for 111 222)
2 110 223 2 null 202 (since record exist in actual table for 110 223)
3 123 250 null 4 51 (since record not exist in actual table but estimate exist for 123 250)
(for 212 312 combination in estimate, since record already processed, no need to process again).
I am using Oracle 11g. Please help me on writing a logic in a single sql query?
Thanks.
There are several ways to write this query. One way is to use join and coalesce:
select coalesce(a.sal_id, e.sal_id) as sal_id,
coalesce(a.gal_id, e.gal_id) as gal_id,
coalesce(a.actual_value, e.estimate_value) as actual_value
from actual a full outer join
estimate e
on a.sal_id = e.sal_id and
a.gal_id = e.gal_id
This assumes that sal_id/gal_id provides a unique match between the tables.
Since you are using Oracle, here is perhaps a clearer way of doing it:
select sal_id, gal_id, actual_value
from (select *,
max(isactual) over (partition by sal_id, gal_id) as hasactual
from ((select 1 as isactual, *
from actual
) union all
(select 0 as isactual, *
from estimate
)
) t
) t
where isactual = 1 or hasactual = 0
This query uses a window function to determine whether there is an actual record with the matching sal_id/gal_id. The logic is to take all actuals and then all records that have no match in the actuals.