How do I calculate percentages with decimals in SQL? - sql

How can i convert this to a decimal in SQL? Below is the equation and i get the answer as 78 (integer) but the real answer is 78.6 (with a decimal) so i need to show this as otherwise the report won't tally up to 100%
(100 * [TotalVisit1]/[TotalVisits]) AS Visit1percent

Try This:
(100.0 * [TotalVisit1]/[TotalVisits]) AS Visit1percent

convert(decimal(5,2),(100 * convert(float,[TotalVisit1])/convert(float,[TotalVisits]))) AS Visit1percent
Ugly, but it works.

CAST(ROUND([TotalVisit1]*100.0/[TotalVisits],2)as decimal(5,2)) as Percentage
Not ugly and work better and fast , enjoy it!

At least in MySQL (if it helps), if you want to use float numbers you had to use a type float field, not the regular int fields.

Just add a decimal to the 100
(100.0 * [TotalVisit1]/[TotalVisits]) AS Visit1percent
this forces all processing to happen in floats... if you want the final output as text, and truncated for display to only one decimal place, use Str function
Str( 100.0 * [TotalVisit1]/[TotalVisits], 4, 1 ) AS Visit1percent

This works perfectly for me:
CAST((1.0 * SUM(ColumnA) /COUNT(ColumnB)) as decimal(5,2))
Hope it helps someone out there in the world.

Its probably overkill to do this, but you may wish to consider casting all values to floats to ensure accuracy at all phases.
(100.0 * ( [TotalVisit1]+0.0 )/([TotalVisits]+0.0) ) as Visit1percent
Note, you really need to put code in here for the case that TotalVisits == 0 or you will get a division by 0 answer.

SELECT(ROUND(CAST(TotalVisit1 AS DECIMAL)/TotalVisits,1)) AS 'Visit1percent'
This will return a decimal and the ROUND will round it to one digit. So in your case you would get 76.6. If you don't want any digits change the 1 to 0 and if you want two digits change it to 2.

Try with this, no round and str or Over(). i found this as a simpler way to do it.
cast((count(TotalVisit1) * 100.0) /TotalVisits as numeric(5,3)) as [Visit1percent]
You can change the number of decimal points as you wish to
e.g. numeric(5,2) or numeric(5,4)

This might not address you issue directly, but when you round a set of numbers for display you're never guaranteed to get numbers that add to 100 unless you take special precautions. For example, rounding 33.33333, 33.33333 and 33.33333 is going to leave you one short on the sum, so the logical thing to do is to modify the percentage for the largest value in the set to take account of any difference.
Here's a way of doing that in Oracle SQL using analytic functions and a subquery factoring (WITH) clause to generate sample data.
with data as (select 25 num from dual union all
select 25 from dual union all
select 25 from dual)
select num,
case
when rnk = 1
then 100 - sum(pct) over (order by rnk desc
rows between unbounded preceding
and 1 preceding)
else pct
end pct
from
(
select num,
round(100*ratio_to_report(num) over ()) pct,
row_number() over (order by num desc) rnk
from data
)
/
NUM PCT
---------------------- ----------------------
25 33
25 33
25 34

In ms Access You can use the SQL function ROUND(number, number of decimals), It will round the number to the given number of decimals:
ROUND((100 * [TotalVisit1]/[TotalVisits]),1) AS Visit1percent

Related

finding percentages between 2 different columns in sql

I was create this query:
select first_price, last_price, cast((sum(1 - (first_price / nullif(last_price,0)))) as double) as first_vs_last_percentages
from prices
group by first_price, last_price
having first_vs_last_percentages >= 0.1
unfortunately this is my wrong data in first_vs_last_percentages col
ID
first_price
last_price
first_vs_last_percentages
1
10
11
1-(10/11) = 1.0
2
66
68
1-(66/68) = 1.0
It was supposed to return this output:
ID
first_price
last_price
first_vs_last_percentages
1
10
11
1-(10/11) = 0.0909
2
66
68
1-(66/68) = 0.0294
if someone has a good solution and it will be in presto syntax it will be wonderful.
It seems you got struck by another case of integer division (your cast to double is a bit late), update the query so the divisor or dividend type changes (for example by multiplying one of them by 1.0 which is a bit shorter then cast to double):
select -- ...
, sum(1 - (first_price * 1.0) / nullif(last_price, 0)) first_vs_last_percentages
from ...
P.S.
Your query is a bit strange, not sure why do you need grouping and sum here.
It depends on which database engine you work upon. Typically, most query confusion rely on either conceptual or syntatic mistakes. In either one or the other cases, it seek to operate a row-percentage double 100.0*(last-first)/first. It means, you can drop the group by and having, since we MUST NOT group by double values, rather intervals they belong.
select
first_price,
last_price,
CASE
WHEN first_price = 0 THEN NULL
ELSE (last_price-first_price)/first_price
end as first_vs_last_percentage
from prices

WHILE Window Operation with Different Starting Point Values From Column - SQL Server [duplicate]

In SQL there are aggregation operators, like AVG, SUM, COUNT. Why doesn't it have an operator for multiplication? "MUL" or something.
I was wondering, does it exist for Oracle, MSSQL, MySQL ? If not is there a workaround that would give this behaviour?
By MUL do you mean progressive multiplication of values?
Even with 100 rows of some small size (say 10s), your MUL(column) is going to overflow any data type! With such a high probability of mis/ab-use, and very limited scope for use, it does not need to be a SQL Standard. As others have shown there are mathematical ways of working it out, just as there are many many ways to do tricky calculations in SQL just using standard (and common-use) methods.
Sample data:
Column
1
2
4
8
COUNT : 4 items (1 for each non-null)
SUM : 1 + 2 + 4 + 8 = 15
AVG : 3.75 (SUM/COUNT)
MUL : 1 x 2 x 4 x 8 ? ( =64 )
For completeness, the Oracle, MSSQL, MySQL core implementations *
Oracle : EXP(SUM(LN(column))) or POWER(N,SUM(LOG(column, N)))
MSSQL : EXP(SUM(LOG(column))) or POWER(N,SUM(LOG(column)/LOG(N)))
MySQL : EXP(SUM(LOG(column))) or POW(N,SUM(LOG(N,column)))
Care when using EXP/LOG in SQL Server, watch the return type http://msdn.microsoft.com/en-us/library/ms187592.aspx
The POWER form allows for larger numbers (using bases larger than Euler's number), and in cases where the result grows too large to turn it back using POWER, you can return just the logarithmic value and calculate the actual number outside of the SQL query
* LOG(0) and LOG(-ve) are undefined. The below shows only how to handle this in SQL Server. Equivalents can be found for the other SQL flavours, using the same concept
create table MUL(data int)
insert MUL select 1 yourColumn union all
select 2 union all
select 4 union all
select 8 union all
select -2 union all
select 0
select CASE WHEN MIN(abs(data)) = 0 then 0 ELSE
EXP(SUM(Log(abs(nullif(data,0))))) -- the base mathematics
* round(0.5-count(nullif(sign(sign(data)+0.5),1))%2,0) -- pairs up negatives
END
from MUL
Ingredients:
taking the abs() of data, if the min is 0, multiplying by whatever else is futile, the result is 0
When data is 0, NULLIF converts it to null. The abs(), log() both return null, causing it to be precluded from sum()
If data is not 0, abs allows us to multiple a negative number using the LOG method - we will keep track of the negativity elsewhere
Working out the final sign
sign(data) returns 1 for >0, 0 for 0 and -1 for <0.
We add another 0.5 and take the sign() again, so we have now classified 0 and 1 both as 1, and only -1 as -1.
again use NULLIF to remove from COUNT() the 1's, since we only need to count up the negatives.
% 2 against the count() of negative numbers returns either
--> 1 if there is an odd number of negative numbers
--> 0 if there is an even number of negative numbers
more mathematical tricks: we take 1 or 0 off 0.5, so that the above becomes
--> (0.5-1=-0.5=>round to -1) if there is an odd number of negative numbers
--> (0.5-0= 0.5=>round to 1) if there is an even number of negative numbers
we multiple this final 1/-1 against the SUM-PRODUCT value for the real result
No, but you can use Mathematics :)
if yourColumn is always bigger than zero:
select EXP(SUM(LOG(yourColumn))) As ColumnProduct from yourTable
I see an Oracle answer is still missing, so here it is:
SQL> with yourTable as
2 ( select 1 yourColumn from dual union all
3 select 2 from dual union all
4 select 4 from dual union all
5 select 8 from dual
6 )
7 select EXP(SUM(LN(yourColumn))) As ColumnProduct from yourTable
8 /
COLUMNPRODUCT
-------------
64
1 row selected.
Regards,
Rob.
With PostgreSQL, you can create your own aggregate functions, see http://www.postgresql.org/docs/8.2/interactive/sql-createaggregate.html
To create an aggregate function on MySQL, you'll need to build an .so (linux) or .dll (windows) file. An example is shown here: http://www.codeproject.com/KB/database/mygroupconcat.aspx
I'm not sure about mssql and oracle, but i bet they have options to create custom aggregates as well.
You'll break any datatype fairly quickly as numbers mount up.
Using LOG/EXP is tricky because of numbers <= 0 that will fail when using LOG. I wrote a solution in this question that deals with this
Using CTE in MS SQL:
CREATE TABLE Foo(Id int, Val int)
INSERT INTO Foo VALUES(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)
;WITH cte AS
(
SELECT Id, Val AS Multiply, row_number() over (order by Id) as rn
FROM Foo
WHERE Id=1
UNION ALL
SELECT ff.Id, cte.multiply*ff.Val as multiply, ff.rn FROM
(SELECT f.Id, f.Val, (row_number() over (order by f.Id)) as rn
FROM Foo f) ff
INNER JOIN cte
ON ff.rn -1= cte.rn
)
SELECT * FROM cte
Not sure about Oracle or sql-server, but in MySQL you can just use * like you normally would.
mysql> select count(id), count(id)*10 from tablename;
+-----------+--------------+
| count(id) | count(id)*10 |
+-----------+--------------+
| 961 | 9610 |
+-----------+--------------+
1 row in set (0.00 sec)

Oracle Number Issue

Good Day,
I have an issue I could use your help with.
A customer would like invoice amounts displayed as follows:
Example Invoice Amounts
-405.12 to be shown as 000000040512
&
-400.00 to be shown as 000000040000
The following query works fine for the 405.12 amount, but for the 400.00 amount it drops the two zeros on the right side
LPAD(REPLACE((invoiceamt*-1),'.',''),12,0)
How may I solve this issue?
Thank You
Aaron
I suggest
to_Char(abs(invoiceamt) * 100, '000000000000')
where
abs - absolute value - get rid of sign (-)
* 100 - removing decimal point
to_Char - final formatting (12 mandatory digits)
Forget the REPLACE, just multiply invoiceamt by -100 and then LPAD to the required length.
Another question has reminded me of the existence of the V format model element:
Returns a value multiplied by 10^n (and if necessary, round it up), where n is the number of 9's after the V.
But you can use zeros instead of 9s to keep leading zeros, and add FM to remove the leading space (which is there for a - symbol for negative values - which you won't have). So you can also do:
with t (n) as (
select -405.12 from dual
union all select -400 from dual
)
select n, to_char(abs(n), 'FM0000000000V00') as result
from t;
N RESULT
---------- -------------
-405.12 000000040512
-400 000000040000

Rounding numbers to the nearest 10 in Postgres

I'm trying to solve this particular problem from PGExercises.com:
https://www.pgexercises.com/questions/aggregates/rankmembers.html
The gist of the question is that I'm given a table of club members and half hour time slots that they have booked (getting the list is a simple INNER JOIN of two tables).
I'm supposed to produce a descending ranking of members by total hours booked, rounded off to the nearest 10. I also need to produce a column with the rank, using the RANK() window function, and sort the result by the rank. (The result produces 30 records.)
The author's very elegant solution is this:
select firstname, surname, hours, rank() over (order by hours) from
(select firstname, surname,
((sum(bks.slots)+5)/20)*10 as hours
from cd.bookings bks
inner join cd.members mems
on bks.memid = mems.memid
group by mems.memid
) as subq
order by rank, surname, firstname;
Unfortunately, as a SQL newbie, my very unelegant solution is much more convoluted, using CASE WHEN and converting numbers to text in order to look at the last digit for deciding on whether to round up or down:
SELECT
firstname,
surname,
CASE
WHEN (SUBSTRING(ROUND(SUM(slots*0.5),0)::text from '.{1}$') IN ('5','6','7','8','9','0')) THEN CEIL(SUM(slots*0.5) /10) * 10
ELSE FLOOR(SUM(slots*0.5) /10) * 10
END AS hours,
RANK() OVER(ORDER BY CASE
WHEN (SUBSTRING(ROUND(SUM(slots*0.5),0)::text from '.{1}$') IN ('5','6','7','8','9','0')) THEN CEIL(SUM(slots*0.5) /10) * 10
ELSE FLOOR(SUM(slots*0.5) /10) * 10
END DESC) as rank
FROM cd.bookings JOIN cd.members
ON cd.bookings.memid = cd.members.memid
GROUP BY firstname, surname
ORDER BY rank, surname, firstname;
Still, I manage to almost get it just right - out of the 30 records, I get one edge case, whose firstname is 'Ponder' and lastname is 'Stephens'. His rounded number of hours is 124.5, but the solution insists that rounding it to the nearest 10 should produce a result of 120, whilst my solution produces 130.
(By the way, there are several other examples, such as 204.5 rounding up to 210 both in mine and the exercise author's solution.)
What's wrong with my rounding logic?
If you want to round to the nearest 10, then use the built-in round() function:
select round(<whatever>, -1)
The second argument can be negative, with -1 for tens, -2 for hundreds, and so on.
To round to the nearest multiple of any number (range):
round(<value> / <range>) * <range>
“Nearest” means values exactly half way between range boundaries are rounded up.
This works for arbitrary ranges, you could round to the nearest 13 or 0.05 too if you wanted to:
round(64 / 10) * 10 —- 60
round(65 / 10) * 10 —- 70
round(19.49 / 13) * 13 -- 13
round(19.5 / 13) * 13 -- 26
round(.49 / .05) * .05 -- 0.5
round(.47 / .05) * .05 -- 0.45
I have struggled with an equivalent issue. I needed to round number to the nearest multiple of 50. Gordon's suggestion here does not work.
My first attempt was SELECT round(120 / 50) * 50, which gives 100. However, SELECT round(130 / 50) * 50 gave 100. This is wrong; the nearest multiple is 150.
The trick is to divide using a float, e.g. SELECT round(130 / 50.0) * 50 is going to give 150.
Turns out that doing x/y, where x and y are integers, is equivalent to trunc(x/y). Where as float division correctly rounds to the nearest multiple.
I don't think Bohemian's formula is correct.
The generalized formula is:
round((value + (range/2))/range) * range
so to convert to nearest 50, round((103 + 25)/50) * 50 --> will give 100
A modified version of the Author's elegant solution that works:
I hope you find it useful
select firstname, surname, round(hrs, -1) as hours, rank() over(order by
round(hrs, -1) desc) as rank
from (select firstname, surname, sum(bks.slots) * 0.5 as hrs
from cd.members mems
inner join cd.bookings bks
on mems.memid = bks.memid
group by mems.memid) as subq
order by rank, surname, firstname;

TSQL Sum is giving wrong result

I want to get sum of data but I am getting wrong result
Example 1
Example 2
Result when doing sum
AS you can see from Example 1 - where p_key is 11020145101617761 and LC_Amount is 8.4 , 168 , -176.4 the sum of this is 0
similarly in Example 2 - where p_key is 1102014510615767 and LC_amount is
-571067.53, 543873.84 , 27193.69 the sum of this is also 0
but in the result when I do group by with p_key , I am not getting 0
I don't understand what is the reason behind this.
It's an example of IEEE-754 rounding errors. Note the numbers are all very close to zero, but juuuuuust off, see the exponent.
Wrap your SUM in ROUND():
SELECT ROUND( SUM( LC_Amount ), 10 )
...should do it.
This occurs because float is not true exact numeric type.
Try to use DECIMAL(10,2) type to avoid round errors.
There is a lot info about float type in the web.