Oracle APex calculate profit on rendering - sql

I have an interactive grid with fixed rows and need to calculate the formula on pre-rendering.
So the source query is:
select kpi,monthly,yearly from kpi where project_id = :P1_PROJECT_ID;
I need to modify this so that the row where kpi='Gross' is calculated on rendering.
It looks in the grid like:
Expected result:(Gross=Profit/Loss)
I am trying to write sql query but it doesn;t work.
Despite data it returns null.
What am i doing wrong here?
select kpi,
case when KPI='Gross'
then to_char(case when KPI='Profit' then to_number(replace(nvl(monthly,0),',','')) end /
case when KPI='Loss' then to_number(replace(nvl(monthly,0),',','')) end ,'999,999,999,999')
else to_char( monthly,'999,999,999,999') end as monthly,
case when KPI='Gross'
then to_char(case when KPI='Profit' then to_number(replace(nvl(yearly,0),',','')) end /
case when KPI='Loss' then to_number(replace(nvl(yearly,0),',','')) end ,'999,999,999,999')
else to_char( yearly,'999,999,999,999') end as yearly,
from kpi where project_id = :P1_PROJECT_ID;
To_char is used to display values as comma separated.
So it would when KPI=Gross, it will divide the columns where kpi=profit by kpi=Loss and dispaly result.
Also the result in the row where KPI=Gros should also have % concatenated.
Apex 20.2
How can this be achieved?

You were close, but you need to use window functions. Without using window functions, the query will not look at the other rows in the case statements to calculate the GROSS column.
The query below is how to properly calculate the GROSS using window functions. I have added ROUND to round the gross to an integer, but you can remove that if you want the decimal points.
WITH
kpi (pk,
kpi,
monthly,
yearly,
project_id)
AS
(SELECT 1, 'Revenue', 60000, 2000000, 1 FROM DUAL
UNION ALL
SELECT 2, 'Profit', 20, 30, 1 FROM DUAL
UNION ALL
SELECT 3, 'Loss', 10, 50, 1 FROM DUAL
UNION ALL
SELECT 4, 'Gross', NULL, NULL, 1 FROM DUAL)
SELECT k.kpi,
CASE k.kpi
WHEN 'Gross'
THEN
ROUND (
SUM (CASE k.kpi WHEN 'Profit' THEN k.monthly ELSE 0 END)
OVER (PARTITION BY project_id)
/ SUM (CASE k.kpi WHEN 'Loss' THEN k.monthly ELSE 0 END)
OVER (PARTITION BY project_id))
|| '%'
ELSE
TO_CHAR (k.monthly)
END AS monthly,
CASE k.kpi
WHEN 'Gross'
THEN
ROUND (
SUM (CASE k.kpi WHEN 'Profit' THEN k.yearly ELSE 0 END)
OVER (PARTITION BY project_id)
/ SUM (CASE k.kpi WHEN 'Loss' THEN k.yearly ELSE 0 END)
OVER (PARTITION BY project_id))
|| '%'
ELSE
TO_CHAR (k.yearly)
END AS yearly
FROM kpi k
WHERE project_id = 1;
KPI MONTHLY YEARLY
__________ __________ __________
Revenue 60000 2000000
Profit 20 30
Loss 10 50
Gross 2% 1%

Related

Turning PIVOT query into table function

Following up on BigQuery pivot on more fields, based on the suggestion by the genius Mikhail, I was wondering if the following 'pivot-like functionality' could be turned into a table function. Here would be an example:
The query from the currently accepted answer is:
select
(case when grp_set & 1 > 0 then Reseller end) as Reseller,
(case when grp_set & 2 > 0 then ProductGroup end) as ProductGroup,
(case when grp_set & 4 > 0 then Product end) as Product,
(case when grp_set & 8 > 0 then Year end) as Year,
(case when grp_set & 16 > 0 then Quarter end) as Quarter,
(case when grp_set & 32 > 0 then Product_Info end) as Product_Info,
sum(Revenue) as Revenue,
sum(Units) as Units
from `first-outlet-750.biengine_tutorial.Product`, unnest(generate_array(1, 64)) grp_set
where Year IN (2020) and Quarter in ('Q1', 'Q2')
group by 1, 2, 3, 4, 5, 6
having not (Quarter is null and Product_Info is not null)
and not (Year is null and Quarter is not null)
and not (ProductGroup is null and Product is not null)
order by 1, 2, 3, 4 , 5, 6
And I'm wondering if a table function could be created along the lines of:
PIVOT(
[row_agg1, row_agg2, ...],
[col_agg1, col_agg2, ...],
[agg_val1, agg_val2, ...]
)
So the above query could hopefully be translated into something like:
SELECT
*
FROM
PIVOT(
[Reseller, ProductGroup, Product], -- rows
[Year, Quarter, ProductInfo], -- cols
[SUM(Revenue), SUM(Units)] -- vals
)
A few things that I think might be a bit tricky:
Where does the actual table go in the FROM clause?
How would aliases work? For example, what if we wanted SUM(Revenue) to show up as TotalRev -- how to easily be able to refer to that in the main SELECT clause to be able to alias it if not just doing a SELECT * ?

How to make multiple aggregate function without break / divided the fields

Is it possible to make an aggregate function without break / divided the grouping fields? I make a query but it will divided into duplicate value in the first field, here is my query:
SELECT TOP 5 empname AS 'EMP Name',
SUM (CASE WHEN prod = 'P' THEN 1 ELSE 0 END) AS 'Count of Prod',
COUNT (prod) AS 'Total Account',
FORMAT (COALESCE (SUM (CASE WHEN prod = 'P' THEN 1 ELSE 0 END) / COUNT (prod), 0), 'P') AS '% Prod',
DATEDIFF(DAY, t_start, t_end) as 'Duration Trip'
FROM Sampletable
WHERE empname NOT IN ('NA') AND
empname IS NOT NULL AND
t_end IS NOT NULL
GROUP BY empname,
prod,
t_end,
t_start
ORDER BY [Count of Prod] DESC
My expected result:
Emp. Name
Count of Prod
Total Account
% Prod
Duration Trip
Emp.1
62
63
98,41%
30
Emp.2
45
48
93,75%
28
Emp.3
20
22
90,91%
25
Emp.4
20
24
83,33%
22
Emp.5
15
19
78,95%
20
Thank you in advance.
If you want one row per empname, then that should be the only column in the group by (or perhaps other columns that describe each employee without multiplying the number of rows).
That suggests something like this:
SELECT TOP 5 empname,
SUM(CASE WHEN prod = 'P' THEN 1 ELSE 0 END) AS prod_count,
COUNT prod) AS Total_Account,
FORMAT(AVG(CASE WHEN prod = 'P' THEN 1.0 ELSE 0 END), '%P') AS prod_ratio,
SUM(DATEDIFF(DAY, t_start, t_end)) as trip_duration
FROM Sampletable
WHERE empname NOT IN ('NA') AND
empname IS NOT NULL AND
t_end IS NOT NULL
GROUP BY empname
ORDER BY prod_count DESC;
Note some of the changes to the query:
The column aliases are simplified so no escape characters are needed.
The GROUP BY has only empname.
The logic for the proportion of products is simplified using AVG().
I assume that the trip duration should be a sum of the individual durations.
I did not remove it, but empname IS NOT NULL is redundant, because the NOT IN handles this.

Divide and sum in SQL

I got this code and in this code I do a sum of the slow and fast driver. My Problem is I must divide this sum with the normal driver. I donĀ“t know how I can do a division in this statement:
Select *
FROM (
Select date as Datetime, tevent.name as Event, level = case
when levelname = 'High' then 'High'
when levelname = 'Normal' then 'Normal'
when shiftname = 'Low' then 'Low'
end, SUM(value) as sum
from tCount inner join tEvent ON tCount.eventid = tevent.id
where Name in ('Drive Fast', 'Drive Slow')
and date > getdate() -1
and tevent.Name in ('E01','E02','E03','E04','E05','E06','E07','E08')
and CalName = 'Drive'
group by tevent.name, date, levelname
) as s
PIVOT
(
SUM(sum)
FOR Event IN (E01,E02,E03,E04,E05,E06,E07,E08)
) as p
order by Datetime, level
And Then I put the same Select statement with the normal driver :
... from tCount inner join tEvent ON tCount.eventid = tevent.id
where Name in ('drive normal') ...
And I would like to make a division like this:
(Sum('drive fast' + 'drive slow')/Sum('drive normal')) * 100
There is a simpler way to include different cases in different sums inside a SQL statement: sum a case, like in the below calculation of percent:
Select ...
, SUM(case Name
when 'drive fast' then Value
when 'drive slow' then value
else 0 end)
/ SUM(case Name
when 'drive normal' then value
else 0 end) * 100 as percentage
from ...
where ...
group by ...;
As I lack data to test this code, I created a query on the CARS table SAS delivers as training material, implementing the same principle.
select Cylinders
, sum(case origin when 'USA' then EngineSize
when 'Asia' then EngineSize
else 0.0 end)
/ sum(case origin when 'Europe' then EngineSize
else 0.0 end)
* 100 as percentage
from sasHelp.cars
where Cylinders in (4, 5, 6, 12)
group by Cylinders

Calculating the sum of annual amounts when frequencies of payments differ

I'm currently trying to write a select statement which calculates the Annual sum in one single column.
The problem I am facing is that there are several different payment frequencies e.g. I would have to multiply a monthly amount by 12 to get the annual, quarterly by 4, semi annual by 2 etc.
I have written a statement below which does this, however it demands that I group by the frequency and amount fields, which gives the undesired result.
select (case when Frequency='month' then SUM(cast(Amount as decimal(10,2))*12)
else (case when Frequency='quarter' then SUM(cast(Amount as decimal(10,2))*4)
else (case when Frequency='year' then SUM(cast(Amount as decimal(10,2))*1)
else (case when Frequency='six months' then SUM(cast(Amount as decimal(10,2))*2) end)
end)
end) end) as 'Total Annual Amount'
from Table group by Frequency
I understand I maybe barking up the wrong tree as far as solving this problem, but the above is the closest I have gotten.
Hopefully I have been descriptive enough, if you need me to elaborate further please let me know
Try moving your sum outside of your case:
select
sum (case when Frequency='Month' then (cast(amount as decimal(10,2))*12
when Frequency='quarter' then...
end) as [Total Annual Amount]
You can also use WITH AS (assuming ORACLE)...
WITH dmap AS
( select 'Month' as duration, 12 as mult from dual
UNION
select 'Year' as duration, 1 as mult from dual
UNION
select 'Quater' as duration, 4 as mult from dual
UNION
select 'six months' as duration, 2 as mult from dual
)
select sum(cast(Amount as decimal(10,2)) * mult)
from tab, dmap
where duration = frequency;

Can I combine my two SQLite SELECT statements into one?

I have a SQLite table called posts. An example is shown below. I would like to calculate the monthly income and expenses.
accId date text amount balance
---------- ---------- ------------------------ ---------- ----------
1 2008-03-25 Ex1 -64.9 3747.56
1 2008-03-25 Shop2 -91.85 3655.71
1 2008-03-26 Benny's -100.0 3555.71
For the income I have this query:
SELECT SUBSTR(date, 0,7) "month", total(amount) "income" FROM posts
WHERE amount > 0 GROUP BY month ORDER BY date;
It works fine:
month income
---------- ----------
2007-05 4877.0
2007-06 8750.5
2007-07 8471.0
2007-08 5503.0
Now I need the expenses and I could of cause just repeat the first statement with the condition amount < 0, but I am wondering if there is an elegant way to get both income and expenses in one query?
Try something like this
select substr(date, 0,7) "Month",
total(case when a > 0 then a else 0 end) "Income",
total(case when a < 0 then a else 0 end) "Expenses"
from posts
group by month
Look into the UNION statement (bottom of the link). This will let you combine the results of two queries, generally in the form:
<SELECT_STATEMENT_1> UNION <SELECT_STATEMENT_2>
Not sure if SQL Lite supports CASE statements, but if it does you could do something like this.
SELECT SUBSTR(date, 0,7) "month"
, total(CASE WHEN Amount > 0 THEN Amount ELSE 0 END) "income"
, -1 * total(CASE WHEN Amount < 0 THEN Amount ELSE 0 END) "expenses"
FROM posts
GROUP BY month
ORDER BY date;