Skip to Main Content
  • Questions
  • Calculating a percentage of the total in SQL*Plus reports

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Ivan.

Asked: October 25, 2001 - 8:02 pm UTC

Last updated: October 25, 2005 - 1:29 am UTC

Version: 8.17

Viewed 10K+ times! This question is

You Asked

Tom,

I can not figure out how to calculate the percentage of a field that was determined by compute

What I want to do is create a report sorted by date and code which I have done below. However the percentage needs to be calculated by dividing the counter by the total (29168). Seems easy enough however the total is a compute value. For code a below the percent should be counter (7467) divided by the total (29168) or (.26). I also need to be able to total the percentage to hopefully get 100%.

Code COUNTER PERCENTAGE MONT
------------------------------ ---------- ---------- ----
A 7467 .26
B 452 xx
C 538 xx
D 300 xx
E 1012 xx
F 111 xx
G 44 xx
T 19244 xx
****************************** ---------- ****
29168

set echo off
set pause off
set termout off
set feedback off
set embedded off
set verify off
set heading on
set trimspool on
set linesize 176
set pagesize 59
set newpage 0
set space 1
col today noprint new_value report_date
col dbname noprint new_value prt_dbname
select to_char(sysdate,'dd-Mon-yy hh24:mi') today
from dual;
clear columns
clear breaks
clear computes
col code format a30 head 'Code'
col substr(change_date,3,2) || substr(change_date,1,2) format a10
TTITLE -
left format a15 report_date -
center 'xxx Change File: ' format a30 -
right 'Page' format 999 SQL.PNO skip 2
break on Month skip 2 on code
compute sum label 'Totals' of counter on Month
spool xxx_change_file.176
SELECT
code,
COUNT(code) as Counter,
COUNT(code) / 100 as Percentage,
substr(change_date,3,2) || substr(change_date,1,2) as Month
FROM
duns_change_canada_us
GROUP BY
substr(change_date,3,2) || substr(change_date,1,2),
code
ORDER BY
substr(change_date,3,2) || substr(change_date,1,2),
code;
spool off

As always I really appreciate all the help you give me and the others.

- Ivan

and Tom said...

Analytic functions are sooooo cooool. If you want to read lots more about them (other then whats in the documentation) I devoted a whole chapter to nothing but in my book:

scott@ORA717DEV.US.ORACLE.COM> break on month skip 2
scott@ORA717DEV.US.ORACLE.COM> compute sum of counter on month
scott@ORA717DEV.US.ORACLE.COM> column pct format 999.99
scott@ORA717DEV.US.ORACLE.COM>
scott@ORA717DEV.US.ORACLE.COM> select owner,
2 counter,
3 round(100*(ratio_to_report(counter) over (partition by month)),2) pct,
4 month
5 from ( select owner, count(owner) counter, to_char(created,'mm') month
6 from all_objects
7 group by to_char(created,'mm'), owner
8 )
9 order by month, owner
10 /

OWNER COUNTER PCT MO
------------------------------ ---------- ------- --
CTXSYS 51 .35 08
PUBLIC 7416 50.89
SCOTT 19 .13
SYS 7086 48.62
SYSTEM 2 .01
---------- **
14574 su


OPS$CLBECK 3 3.66 09
PUBLIC 29 35.37
SCOTT 31 37.80
SYS 3 3.66
SYSTEM 16 19.51
---------- **
82 su


COMMON 1 1.16 10
PUBLIC 1 1.16
SCOTT 48 55.81
SYS 35 40.70
TYPES 1 1.16
---------- **
86 su



Rating

  (6 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

New Function to Play with

Ivan, October 26, 2001 - 4:08 pm UTC

I had no idea about the ratio_to_report function and your example answers my questions.

Thanks again,

Ivan

Analytics are fun

Josh, January 16, 2003 - 3:10 pm UTC

Tom,

How could I use this to give me a simple ratio? For example:

(select count(*) from table where col_a = 'Y')/(select count(*) from table)

Tom Kyte
January 16, 2003 - 7:58 pm UTC

don't know what you mean.

For Josh.....

A Reader, January 17, 2003 - 7:14 am UTC

I think Josh means, can you use it to get a ratio of:
- values meeting criteria in a table / total rows in table

as a 'one-off' value.

Here's a nasty example which gives the ratio of invalid to all objects from the all_objects table - I'm sure there's a better way:

select the_ratio from (
select status, count(*), ratio_to_report(count(*)) OVER () the_ratio
from all_objects
group by status
) where status != 'VALID'


Regards,

Paul

Tom Kyte
January 17, 2003 - 9:29 am UTC

ahh, ok, i would do this:


select sum(decode(col_a,'Y',1,0))/count(*) from table;


or more readable:

select sum( (case when col_a = 'Y' then 1 else 0 end) ) / count(*) from table;



Still not getting it.

DanielD, October 24, 2005 - 6:51 pm UTC

Tom,

I see your solution, even looked up Oracle8i documenation for RATIO_TO_REPORT function, but still don't know how to calculate percentage of TOTAL_HOURS against grant total for developer. Here is the code I have so far:

BREAK ON username skip 2
COMPUTE SUM OF TOTAL_HOURS ON username
COLUMN TOTAL_HOURS FOR 999.99

SELECT USERNAME
,PROJECTNAME
,CLIENTNAME
,TASKTIMEOFFNAME
,SUM (TOTAL_HOURS) as TOTAL_HOURS
,DEPARTMENTNAME
FROM rp_v_timesheetdetail
WHERE TO_CHAR(entrydate, 'MM') = TO_CHAR(SYSDATE, 'MM')
GROUP BY username
,projectname
,clientname
,tasktimeoffname
,departmentname;

What I am getting with this sql is:

Usename, First DCR Client1 Cost of Development 1.00 Development
OI/OC Client2 Hosting 11.50 Development
OI/OC Client1 Cost of Development 104.50 Development
******************** -----------
sum 117.00

and I would like to get:

Usename, First DCR Client1 Cost of Development 1.00 0.85 Development
OI/OC Client2 Hosting 11.50 9.83 Development
OI/OC Client1 Cost of Development 104.50 89.32 Development
******************** -----------
sum 117.00

Where values 0.85, 9.83, and 89.32 are being calculated from sum for each user (Username, First - 117.00).

Thank you very much for your time.

Tom Kyte
October 25, 2005 - 1:29 am UTC

sorry - no create tables, no insert intos.....


but isn't it just ratio to report of your sum()

take your query, call it Q


select q.*, 100*ratio_to_report(total_hours) over ()
from (Q) q
/

Got it now

DanielD, October 25, 2005 - 11:16 am UTC

Tom,

Sorry for not providing enough info. When I did apply your example on my query I got percentage, but for whole report. So I've combined it with your first example and I got query that does what I want:

BREAK ON username skip 2
COMPUTE SUM OF TOTAL_HOURS ON username
COLUMN TOTAL_HOURS FOR 999.99
SELECT username
,projectname
,clientname
,tasktimeoffname
,total_hours
,round(100*(ratio_to_report(percent_hours) over (partition by username)),2) as percent
,departmentname
FROM (SELECT username
,projectname
,clientname
,tasktimeoffname
,SUM (total_hours) as total_hours
,SUM (total_hours) as percent_hours
,departmentname
FROM rp_v_timesheetdetail
WHERE TO_CHAR(entrydate, 'MM') = TO_CHAR(SYSDATE, 'MM')
GROUP BY username
,projectname
,clientname
,tasktimeoffname
,departmentname
);

Thank you!

DanielD

Subash Reddy, June 13, 2006 - 4:43 pm UTC


More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.