Cumulative sum with condition - qlikview

I wish to derive cumulative column based on initially adding the daily amount to the Value, then subsequently adding the daily amount to the resulting figure.
Could you please help, thanks.
Date
Type
Value
Rate
Cummulative
29/04/2022
A
128.61
32.00
256.61
28/04/2022
A
128.61
32.00
224.61
27/04/2022
A
128.61
32.00
192.61
26/04/2022
A
128.61
32.00
160.61

Have a look at the example script below. Once reloaded the CumulativeData table will contain new column Cumulative which will be the result.
The "magic" is happening in the following expression:
if(RecNo() = 1,
Value + Rate,
peek(Cumulative) + Rate
) as Cumulative
In the expression we are saying:
if the record number is 1 (first row of the table) then sum Value and Rate values. This is our "base" and we'll accumulate to this value
for the next rows get the above (previous row) value of Cumulative column (peek(Cumulative)) and add the current row Rate value
More about peek function can be found at the documentation page
Example script:
RawData:
Load * Inline [
Date , Type, Value , Rate
29/04/2022, A , 128.61, 32.00
28/04/2022, A , 128.61, 32.00
27/04/2022, A , 128.61, 32.00
26/04/2022, A , 128.61, 32.00
];
// Dont foget to order the table in ascending order
CumulativeData:
Load
*,
if(RecNo() = 1,
Value + Rate,
peek(Cumulative) + Rate
) as Cumulative
Resident
RawData
Order By
Date ASC
;
Drop Table RawData;
Result table:

Related

Issue to build sql query to filter data from one table

I am new to SQL and I have the following table defined in Oracle:
Basically I need to create a store procedure that does the following things:
Display the code,amount, currency and occurrence fields.
However the identical code that have the same currency add the amount and show them in one row and their occurrence, for instance from that data of the above table the result should be:
code: TS-4.1 ,Amount: 150 ,Currency : USD, Occurrence: 2
code: TS-4.1 ,Amount: 25 ,Currency : EU , Occurrence: 1
code: TS-2.1 ,Amount: 225 ,Currency : USD, Occurrence: 1
...other result
Any idea how this can be achieved please?
SELECT CODE, SUM(AMOUNT), CURRENCY, COUNT(*) as Occurrence
FROM <your_table>
GROUP BY CODE, CURRENCY
You can use a GROUP BY.
This would group your entries per code and amount and will display the number of rows that have the same code, amount and currency.

SQL Server - selecting an item based on the previous counter value (same foreign key)

Not sure how to word this above so hopefully this will explain it better. I currently have a table of data as follows which is fetched using this query (the query is looking at a view)
CODE
SELECT
AppRunningPercentages.ProjectID,
AppRunningPercentages.AppID,
AppRunningPercentages.AppCounter,
AppRunningPercentages.PercentageComplete,
RunningPercentage= NULL
from AppRunningPercentages
where ProjectID = 123
DATA
ProjectID(FK) AppID AppCounter PercentageComplete RunningPercentage
123 1 1 50%
123 4 2 40%
123 7 3 10%
Based on my SELECT Statement the values above are shown, however I unsure on how to display the RunningPercentage. based on the above scenario I would like the table below to calculate them as follows within the same SELECT statement however I am unsure on how I can achieve this running total.
RunningPercentage
0%
50%
90%
when the AppCounter = 1, then I want the RunningPercentage to display as 0. This is so I can calculate a value correctly to the current percentage. It is effectively adding the previous percentages together, so when AppCounter = 1, then it is looking for an AppCounter with the value of 0.
When AppCounter = 2, it will add the 0% and the 50% together (50%)
When AppCounter = 3, it will add the 0%, 50% and 40% together (90%)
......And so on
Thankyou for any help with this
In SQL Server 2012+, you would use a cumulative sum:
select t.*,
(sum(PercentageComplete) over (partition by projectid
order by appcounter
) - PercentageComplete
) as RunningPercentage
from t;
Note: you can use a rows between clause instead of subtracting the value in the current row. I find subtracting the value in the current row to be simpler for this logic.
In early versions, you can use outer apply:
select t.*, coalesce(RunningPercentage, 0)
from t outer apply
(select sum(PercentageComplete) as RunningPercentage
from t t2
where t2.projectid = t.projectid and t2.appcounter < t.appcounter
) t2;

Complicate SQL Amount split by percentage in same row transpose (Pivot)?

I am struggling to split total amount field into percentage in the same row and then update the last column with Amount type for which the percentage is applied.
Example data
Total Amount | UF% | UFI% |RA% |RL% |NP% | AmountType
100 |0.00 |20 |9.15 |0.75 |70.01
1520.23 |64.4 |19.1 |15.5 |0.25 |0.75
158520.03|13.25 |35 |2.25 |19.28 |30.22
I have to get percentage of total amount column and then transpose insert them as additional rows in the same table and upate the last column what type of amount it is.
For example for 1st row I can get 5 new rows
Total Amount Amount type
0 UF%
20 UFI%
9.15 RA%
0.75 RL%
70.01 NP%
I am one step at a time to I have created 5 new columns to calculate the percentage as TotalAmount UF%, TotalAmount UFI%, TotalAmountRA% and so on…
Selec t [Total Amount]* UF% as [TotalAmount UF%] … and so on.
I am stuck here shall I use Pivot/unpivot? Or case ?
Or is it any other easier way to use row over partition by ?
Please suggest.
this should work for you. Just copy this into an empty query window and execute. Adapt to your needs...
EDIT: Calculate percentages...
declare #amounts table (TotalAmount decimal(8,2),[UF%] decimal(4,2), [UFI%] decimal(4,2)
,[RA%] decimal(4,2),[RL%] decimal(4,2)
,[NP%] decimal(4,2));
insert into #amounts values
(100,0.00,20,9.15,0.75,70.01)
,(1520.23,64.4,19.1,15.5,0.25,0.75)
,(158520.03,13.25,35,2.25,19.28,30.22);
select up.TotalAmount
,up.Percentag
,(up.TotalAmount/100)*up.Percentag AS AmountPercentage
,up.Amount AS AmountType
from
(
select *
from #amounts
) AS tbl
unpivot
(
Percentag FOR Amount IN([UF%],[UFI%],[RA%],[RL%],[NP%])
) AS up

SQL Rounding Percentages to make the sum 100% - 1/3 as 0.34, 0.33, 0.33

I am currently trying to split one value with percentage column. But as most of percentages values are 1/3, I am not able to get aboslute 100% with two decimal points in the value. For example:
Product Supplier percentage totalvalue customer_split
decimal(15,14) (decimal(18,2) decimal(18,2)
-------- -------- ------------ --------------- ---------------
Product1 Supplier1 0.33 10.00 3.33
Product1 Supplier2 0.33 10.00 3.33
Product1 Supplier3 0.33 10.00 3.33
So, here we are missing 0.01 in the value column and suppliers would like to put this missing 0.01 value against any one of the supplier randomly. I have been trying to get this done in a two sets of SQLs with temporary tables, but is there any simple way of doing this. If possible how can I get 0.34 in the percentage column itself for one of the above rows? 0.01 is negligible value, but when the value column is 1000000000 it is significant.
It sounds like you're doing some type of "allocation" here. This is a common problem any time you are trying to allocate something from a higher granulartiy to a lower granularity, and you need to be able to re-aggregate to the total value correctly.
This becomes a much bigger problem when dealing with larger fractions.
For example, if I try to divide a total value of, say $55.30 by eight, I get a decimal value of $6.9125 for each of the eight buckets. Should I round one to $6.92 and the rest to $6.91? If I do, I will lose a cent. I would have to round one to $6.93 and the others to $6.91. This gets worse as you add more buckets to divide by.
In addition, when you start to round, you introduce problems like "Should 33.339 be rounded to 33.34 or 33.33?"
If your business logic is such that you just want to take whatever remainder beyond 2 significant digits may exist and add it to one of the dollar values "randomly" so you don't lose any cents, #Diego is on the right track with this.
Doing it in pure SQL is a bit more difficult. For starters, your percentage isn't 1/3, it's .33, which will yield a total value of 9.9, not 10. I would either store this as a ratio or as a high-precision decimal field (.33333333333333).
P S PCT Total
-- -- ------------ ------
P1 S1 .33333333333 10.00
P2 S2 .33333333333 10.00
P3 S3 .33333333333 10.00
SELECT
BaseTable.P, BaseTable.S,
CASE WHEN BaseTable.S = TotalTable.MinS
THEN BaseTable.BaseAllocatedValue + TotalTable.Remainder
ELSE BaseTable.BaseAllocatedValue
END As AllocatedValue
FROM
(SELECT
P, S, FLOOR((PCT * Total * 100)) / 100 as BaseAllocatedValue,
FROM dataTable) BaseTable
INNER JOIN
(SELECT
P, MIN(S) AS MinS,
SUM((PCT * Total) - FLOOR((PCT * Total * 100)) / 100) as Remainder,
FROM dataTable
GROUP BY P) as TotalTable
ON (BaseTable.P = TotalTable.P)
It appears your calculation is an equal distribution based on the total number of products per supplier. If it is, it may be advantageous to remove the percentage and instead just store the count of items per supplier in the table.
If it is also possible to store a flag indicating the row that should get the remainder value applied to it, you could assign based on that flag instead of randomly.
run this, it will give an idea on how you can solve your problem.
I created a table called orders just with an ID to be easy to understand:
create table orders(
customerID int)
insert into orders values(1)
go 3
insert into orders values(2)
go 3
insert into orders values(3)
go 3
these values represent the 33% you have
1 33.33
2 33.33
3 33.33
now:
create table #tempOrders(
customerID int,
percentage numeric(10,2))
declare #maxOrder int
declare #maxOrderID int
select #maxOrderID = max(customerID) from orders
declare #total numeric(10,2)
select #total =count(*) from orders
insert into #tempOrders
select customerID, cast(100*count(*)/#total as numeric(10,2)) as Percentage
from orders
group by customerID
update #tempOrders set percentage = percentage + (select 100-sum(Percentage) from #tempOrders)
where customerID =#maxOrderID
this code will basically calculate the percentage and the order with the max ID, then it gets the diference from 100 to the percentage sum and add it to the order with the maxID (your random order)
select * from #tempOrders
1 33.33
2 33.33
3 33.34
This should be an easy task using Windowed Aggregate Functions. You probably use them already for the calculation of customer_split:
totalvalue / COUNT(*) OVER (PARTITION BY Product) as customer_split
Now sum up the customer_splits and if there's a difference to total value add (or substract) it to one random row.
SELECT
Product
,Supplier
,totalvalue
,customer_split
+ CASE
WHEN COUNT(*)
OVER (PARTITION BY Product
ROWS UNBOUNDED PRECEDING) = 1 -- get a random row, using row_number/order you might define a specific row
THEN totalvalue - SUM(customer_split)
OVER (PARTITION BY Product)
ELSE 0
END
FROM
(
SELECT
Product
,Supplier
,totalvalue
,totalvalue / COUNT(*) OVER (PARTITION BY Product) AS customer_split
FROM dropme
) AS dt
After more than one trial and test i think i found better solution
Idea
Get Count of all(Count(*)) based on your conditions
Get Row_Number()
Check if (Row_Number() value < Count(*))
Then select round(curr_percentage,2)
Else
Get sum of all other percentage(with round) and subtract it from 100
This steps will select current percentage every time EXCEPT Last one will be
100 - the sum of all other percentages
this is part of my code
Select your_cols
,(Select count(*) from [tbl_Partner_Entity] pa_et where [E_ID] =#E_ID)
AS cnt_all
,(ROW_NUMBER() over ( order by pe.p_id)) as row_num
,Case when (
(ROW_NUMBER() over ( order by pe.p_id)) <
(Select count(*) from [tbl_Partner_Entity] pa_et where [E_ID] =#E_ID))
then round(([partnership_partners_perc]*100),2)
else
100-
((select sum(round(([partnership_partners_perc]*100),2)) FROM [dbo].
[tbl_Partner_Entity] PEE where [E_ID] =#E_ID and pee.P_ID != pe.P_ID))
end AS [partnership_partners_perc_Last]
FROM [dbo].[tbl_Partner_Entity] PE
where [E_ID] =#E_ID

Trouble writing a query to select one row per "date", given certain conditions

I am having trouble writing a query to select one row per "date", given certain conditions. My table has this structure:
ID date expiration callput iv delta
1 1/1/2009 1/20/2009 C 0.4 0.61
2 1/1/2009 1/20/2009 C 0.3 0.51
3 1/1/2009 2/20/2009 C 0.2 0.41
I would like to write a query with the following characteristics:
For each row, calculate the "days", i.e. the expiration date minus the date. For instance, for row one, the "days" is 19 (1/20 minus 1/1)
The result set should only have rows with a "days" of between 15 and 50
The "callput" value must be "C"
For each date, show only one row. That row should have the following characteristics:
The delta should be greater than 0.5
The delta should be the smallest number greater than 0.5
If there are two rows, the row with the lower days should be selected
Here is 'days' for the sample data above:
ID date expiration days callput iv delta
1 1/1/2009 1/20/2009 19 C 0.4 0.61
2 1/1/2009 1/20/2009 19 C 0.3 0.51
3 1/1/2009 2/20/2009 50 C 0.2 0.41
For my sample dataset, the answer should be row 2, because row 2's "delta" is above 0.5, row 2's delta of 0.51 is closer to 0.5 than row 1's 0.61, and row 2's "days" of 19 is less than row 3's "days" of 50.
This is the query I've written so far:
SELECT date, Min(delta) AS MaxOfdelta, [expiration]-[date] AS days
FROM RAWDATA
WHERE (((delta)>0.5) AND ((callput)="C") AND (([expiration]-[date])>=15 And ([expiration]-[date])<=50))
GROUP BY date, [expiration]-[date]
ORDER BY date;
This works somewhat, but sometimes, there are multiple rows for one date, because two rows on a given day can have a "days" between 15 and 50. I can't get my query to obey the rule "If there are two rows, the row with the lower days should be selected". I would also like the "iv" value for that row to be present in my query result set.
I happen to be using Microsoft Access, but syntax for any SQL engine would be appreciated! :-)
What you can do is select the right rows in a subquery. This query should find the rows you're looking for:
select [date], min([expiration]-[date])
from rawdata
where delta > 0.5
and callput = 'C'
and [expiration]-[date] between 15 and 50
group by [date]
To find the delta that belongs to these rows, put it in a subquery and join on it:
select *
from rawdata
inner join (
select [date]
, min([expiration]-[date]) as days
from rawdata
where delta > 0.5
and callput = 'C'
and [expiration]-[date] between 15 and 50
group by [date]
) as filter
on filter.date = rawdata.date
and filter.days = rawdata.[expiration] - rawdata.[date]
where delta > 0.5
and callput = 'C'
To search for the lowest delta within rows with identical "days", you could add another subquery:
select
SubDaysDelta.date
, SubDaysDelta.MinDays
, SubDaysDelta.MinDelta
, min(rawdata.iv) as MinIv
from rawdata
inner join (
select
SubDays.date
, SubDays.MinDays
, min(delta) as MinDelta
from rawdata
inner join (
select [date]
, min([expiration]-[date]) as MinDays
from rawdata
where delta > 0.5
and callput = 'C'
and [expiration]-[date] between 15 and 50
group by [date]
) as SubDays
on SubDays.date = rawdata.date
and SubDays.MinDays = rawdata.[expiration] - rawdata.[date]
where delta > 0.5
and callput = 'C'
group by SubDays.date, SubDays.MinDays
) as SubDaysDelta
on SubDaysDelta.date = rawdata.date
and SubDaysDelta.MinDays = rawdata.[expiration] - rawdata.[date]
and SubDaysDelta.MinDelta = rawdata.delta
where delta > 0.5
and callput = 'C'
group by SubDaysDelta.date, SubDaysDelta.MinDays, SubDaysDelta.MinDelta
The first subquery "SubDays" searches for rows with the lowest "days". The second subquery "SubDaysDelta" searches for the lowest delta within the "SubDays" set. The outer query filters any duplicates remaining.
It would be more readable and maintainable if you'd use views. The first view could filter on callput and the 15-20 "days" limit. That'd make it a lot easier.
VBA!
I wish I could be as thorough, dedicated and helpful a servant as Andomar. I can only up-vote his answer in sheer awe of him.
However ... I would point out there are perhaps compelling reasons to switch to VBA. Even if you are new to VBA, the benefits in control and trouble shooting may put you ahead. And I'd guess any new learning will help elsewhere in your project.
I wish I would provide a complete answer as Andomar did. But give it a whack.