Calculate dollars to millions returns integer - sql

I am trying to calculate dollar amount (in DB as Decimal(15, 0) NOT NULL) to millions by dividing but as result I get only integer amount.
desired result:
AMOUNT AMOUNT IN MIL
123000 0.1
1123000 1.123
I have this, but it return only 0, 1...
SELECT ... AMOUNT / 1000000 AS "AMOUNT IN MIL" FROM ....

Try to change the datatype to 6 fractional digits before calculation:
cast(amount as decimal(28,6)) / 1000000

You should familiarize yourself with rules of such arithmetic operations. Refer to the Expressions article.
Briefly:
INT / INT = INT
DEC(p, s) / DEC(p', s') = DEC(31, 31-p+s-s')
So, if you want to get DEC(31, X) on INT / INT, you may explicitly cast a numerator to DEC(31-X).
In your case (X=6):
> db2 describe dec(1, 25)/1000000
Column Information
Number of columns: 1
SQL type Type length Column name Name length
-------------------- ----------- ------------------------------ -----------
484 DECIMAL 31, 6 1 1

Related

Cumulative sum based on same column calculated result

I have the following table, for which I am trying to calculate a running balance, and remaining value, but the remaining value is the function of the previously calculated row, as such:
date PR amount total balance remaining_value
----------------------------------------------------------
'2020-1-1' 1 1.0 100.0 1.0 100 -- 100 (inital total)
'2020-1-2' 1 2.0 220.0 3.0 320 -- 100 (previous row) + 220
'2020-1-3' 1 -1.5 -172.5 1.5 160 -- 320 - 160 (see explanation 1)
'2020-1-4' 1 3.0 270.0 4.5 430 -- 160 + 270
'2020-1-5' 1 1.0 85.0 5.5 515 -- 430 + 85
'2020-1-6' 1 2.0 202.0 7.5 717 -- 575 + 202
'2020-1-7' 1 -4.0 -463.0 3.5 334.6 -- 717 - 382.4 (see explanation 2)
'2020-1-8' 1 -0.5 -55.0 3.0 ...
'2020-1-9' 1 2.0 214.0 5.0
'2020-1-1' 2 1.0 100 1.0 100 -- different PR: start new running total
The logic is as follows:
For positive amount rows, the remaining value is simply the value from the previous row in column remaining_value + the value in column total from that row.
For negative amount rows, it gets tickier:
Explanation 1: We start with 320 (previous row balance) and from it we remove 1.5/3.0 (absolute value of current row amount divided by previous row balance) and we multiply it by the previous row remaining_value, which is 320. The calculation gives:
320 - (1.5/3 * 320) = 160
Explanation 2: Same logic as above. 717 - (4/7.5 * 717) = 717 - 382.4
4/7.5 here represents the current row's absolute amount divided by the previous row's balance.
I tried the window function sum() but did not manage to get the desired result. Is there a way to get this done in PostgreSQL without having to resort to a loop?
Extra complexity: There are multiple products identified by PR (product id), 1, 2 etc. Each need their own running total and calculation.
You could create a custom aggregate function:
CREATE OR REPLACE FUNCTION f_special_running_sum (_state numeric, _total numeric, _amount numeric, _prev_balance numeric)
RETURNS numeric
LANGUAGE sql IMMUTABLE AS
'SELECT CASE WHEN _amount > 0 THEN _state + _total
ELSE _state * (1 + _amount / _prev_balance) END';
CREATE OR REPLACE AGGREGATE special_running_sum (_total numeric, _amount numeric, _prev_balance numeric) (
sfunc = f_special_running_sum
, stype = numeric
, initcond = '0'
);
The CASE expression does the split: If amount is positive, just add total, else apply your (simplified) formula:
320 * (1 + -1.5 / 3.0) instead of 320 - (1.5/3 * 320), i.e.:
_state * (1 + _amount / _prev_balance)
Function and aggregate parameter names are only for documentation.
Then your query can look like this:
SELECT *
, special_running_sum(total, amount, prev_balance) OVER (PARTITION BY pr ORDER BY date)
FROM (
SELECT pr, date, amount, total
, lag(balance, 1, '1') OVER (PARTITION BY pr ORDER BY date) AS prev_balance
FROM tbl
) t;
db<>fiddle here
We need a subquery to apply the first window function lag() and fetch the previous balance into the current row (prev_balance). I default to 1 if there is no previous row to avoid NULL values.
Caveats:
If the first row has a negative total, the result is undefined. My aggregate function defaults to 0.
You did not declare data types, nor requirements regarding precision. I assume numeric and aim for maximum precision. The calculation with numeric is precise. But your formula produces fractional decimal numbers. Without rounding, there will be a lot of fractional digits after a couple of divisions, and the calculation will quickly degrade in performance. You'll have to strike a compromise between precision and performance. For example, doing the same with double precision has constant performance.
Related:
Cumulative adding with dynamic base in Postgres

Calculate average of a row in SQL Server

I have the below table..the percent column is of type nvarchar
Data Percent1 Percent2 Percent3
1 3% 4% 6%
2 6% 8% 7%
3 8% 6% 8%
I have to calculate the Avg per line so I get results like
Data Avg
1 4.33%
I was trying to convert the %column into decimal so I can apply the average function
select
Case WHEN Isnumeric([Percent1]) = 1
THEN CONVERT(DECIMAL(18,2),Replace([Percent1],'%',''))
ELSE 0 END AS Percent1
from DashboardData
but I am just getting 0 values..I am guessing the outer function is running before the inner for some reason. Can someone please tell me how I can achieve this.
I know the IsNumeric function will make it 0 but I tried it before that and I was getting an exception that type is not a number.
Thanks
SELECT ISNUMERIC('3%') will return 0, as will all the rest of your values, so your else condition will always be the result.
Just drop the %
select
data,
(replace(Percent1,'%','') + replace(Percent2,'%','') + replace(Percent3,'%','')) * 1.0 / 3
Note, if any of these values are NULL you need to account for that because NULL + anything IS NULL.
Also, you don't want to lean on ISNUMERIC too heavy... it can produce some results you probably aren't expecting
select
ISNUMERIC('$') --money which is a numeric value
,ISNUMERIC('1e4') --scientific notation
,ISNUMERIC('45D-1') --old scientific notation
,ISNUMERIC('.') --just the decimal portion of a float / decimal
Is this what you want?
select dd.*, s.average
from dashboarddata dd cross apply
(select avg(try_convert(numeric(10, 2), replace(pc, '%', ''))) as average
from values (percent1), (percent2), (percent3)) as v(pc)
) s;

Redshift division result does not include decimals

I'm trying to do something really quite basic to calculate a kind of percentage between two columns in Redshift. However, when I run the query with an example the result is simply zero because the decimals are not being covered.
code:
select 1701 / 84936;
Output:
I tried :
select cast(1701 / 84936 as numeric (10,10));
but the result was 0.0000000000.
How could I solve this silly thing?
It is integer division. Make sure that at least one argument is: NUMERIC(accurate data type)/FLOAT(caution: it's approximate data type):
/ division (integer division truncates the result)
select 1701.0 / 84936;
-- or
SELECT 1.0 * 1701 / 84936;
-- or
SELECT CAST(1701 AS NUMERIC(10,4))/84936;
DBFiddle Demo
When mixing data types the order counts
Note that the order of the elements in a math expression counts for the data type of the result.
Let's assume that we intend to calculate the percentage unit_sales/total_sales where both columns (or numbers) are integers.
See and try with this code here.
-- Some dummy table
drop table if exists sales;
create table sales as
select 3 as unit_sales, 9 as total_sales;
-- The calculations
select
unit_sales/total_sales*100, --> 0 (integer)
unit_sales/total_sales*100.0, --> 0.0 (float)
100.0*unit_sales/total_sales --> 33.3 (float and expected result)
from sales;
The output
0 | 0.0 | 33.33
The first column is 0 (integer) because of 3/9=0 in an integer division.
The second column is 0.0 because SQL first got the integer 0 (3/9), and later, SQL converts it to float in order to perform the multiplication by 100.0.
The expected result.
The non-integer 100.0 at the beginning of the expression force a non-integer calculation.

Calculate percentage in MS SQL Server with int columns

I am trying to calculate the total amount for charity. Each row has a bid amount and a charity percentage.
The structure of the fields looks like this:
CurrentBid INT
CharityPercentage INT <-- 0-100, where 100 is donating all to charity
So i tried this:
SELECT CurrentBid, CharityPercentage, CurrentBid * (CharityPercentage / 100) AS TotalCharity FROM Items
However it yields this:
50 100 50
70 100 70
25 50 0
50 25 0
30 10 0
55 15 0
It works as long as the percentage is 100. Anything below and its 0. My guess is that it has to do with the int column.
Any ideas on how to solve it? I have also tried to convert the int using a CAST in the select statement but that did not work. Its a very big procedure to change the INT column to something else.
Explicitly make at least one value a decimal/float/etc. In your case, you already have a literal numeric value, 100; so, simply change it to, 100.0:
CurrentBid * CharityPercentage / 100.0 AS TotalCharity
don't CAST -- instead, just multiply by 1.00. This does the CASTing implicitly.
SELECT CurrentBid, CharityPercentage, (1.00 * CurrentBid * CharityPercentage) / 100 AS TotalCharity FROM Items

Finding even or odd ID values

I was working on a query today which required me to use the following to find all odd number ID values
(ID % 2) <> 0
Can anyone tell me what this is doing? It worked, which is great, but I'd like to know why.
ID % 2 is checking what the remainder is if you divide ID by 2. If you divide an even number by 2 it will always have a remainder of 0. Any other number (odd) will result in a non-zero value. Which is what is checking for.
For finding the even number we should use
select num from table where ( num % 2 ) = 0
As Below Doc specify
dividend % divisor
Returns the remainder of one number divided by another.
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/modulo-transact-sql#syntax
For Example
13 % 2 return 1
Next part is <> which denotes Not equals.
Therefor what your statement mean is
Remainder of ID when it divided by 2 not equals to 0
Be careful because this is not going to work in Oracle database. Same Expression will be like below.
MOD(ID, 2) <> 0
ID % 2 reduces all integer (monetary and numeric are allowed, too) numbers to 0 and 1 effectively.
Read about the modulo operator in the manual.
In oracle,
select num from table where MOD (num, 2) = 0;
dividend % divisor
Dividend is the numeric expression to divide. Dividend must be any expression of integer data type in sql server.
Divisor is the numeric expression to divide the dividend. Divisor must be expression of integer data type except in sql server.
SELECT 15 % 2
Output
1
Dividend = 15
Divisor = 2
Let's say you wanted to query
Query a list of CITY names from STATION with even ID numbers only.
Schema structure for STATION:
ID Number
CITY varchar
STATE varchar
select CITY from STATION as st where st.id % 2 = 0
Will fetch the even set of records
In order to fetch the odd records with Id as odd number.
select CITY from STATION as st where st.id % 2 <> 0
% function reduces the value to either 0 or 1
It's taking the ID , dividing it by 2 and checking if the remainder is not zero; meaning, it's an odd ID.
<> means not equal. however, in some versions of SQL, you can write !=