How to get percentages? - sql

SELECT
date,
location,
total_cases,
total_deaths,
(total_deaths/total_cases)* 100 as Death_percentage
FROM CovidDeaths
ORDER BY 1,2
From my query (to get percentage of COVID deaths per location) death_percentage column returns 0 for every row. What am I doing wrong?

The problem is integer division. Since both operands are integers, SQLite returns an integer result (basically the floor of the result of the division).
SELECT ( 2 / 3 ) * 100;
--> 0
Here, since the denominator of the division is always greater than the numerator, the division always yields 0. We can work around this with a little trick, by turning the 100 multiplier to a decimal value and putting it first in the calculation. It is also important to remove the parentheses around the division, so the decimal typing properly "propagates" to the division:
SELECT 100.0 * 2 / 3
--> 66.666667
In your query:
SELECT
date,
location,
total_cases,
total_deaths,
100.0 * total_deaths / nullif(total_cases, 0) as death_percentage
FROM CovidDeaths
ORDER BY 1,2
Note that the expression also adresses the possibility of a 0 values in the total_cases column - which would otherwise generate an arithmetic error, as pointed out by Jonas Metzler in the comments.

Related

SQL calculation limited to 6 decimal points

I am trying to calculate the percent of total using partition over, and need the result to calculate beyond 6 decimal places. I have millions of records, so I need the calculation to extend beyond 6 decimal places to sum to 100% of total.
I have tried to cast the result as decimal with 18 places, but I am still getting all zeros beyond the 6th decimal place.
Any help would be appreciated!
SELECT
ISNULL(SUM(c.PaidOriginal),0) / SUM(NULLIF(SUM(c.PaidOriginal),0) ) OVER (PARTITION BY c.calyr) AS percentoftotal
,CAST(ISNULL(SUM(c.PaidOriginal),0) / SUM(NULLIF(SUM(c.PaidOriginal),0) ) OVER (PARTITION BY c.calyr) AS DECIMAL(38,18)) AS percentoftotal
FROM c
[screenshot of result]
Update: Thank you for the responses! I have tried casting everything as decimal (the numerator, denominator, and result) and am still getting all zeros past the 6th decimal place. Perhaps the issue is with the Partition by?
CAST(CAST(ISNULL(SUM(c.PaidOriginal),0) AS DECIMAL(38,18)) / CAST(SUM(NULLIF(SUM(c.PaidOriginal),0) ) OVER (PARTITION BY c.calyr)as decimal(38,18)) as decimal(38,18)) AS percentoftotal
cast first or second number to the precision you want before deviding:
SELECT
ISNULL(SUM(c.PaidOriginal),0) / SUM(NULLIF(SUM(c.PaidOriginal),0) ) OVER (PARTITION BY c.calyr) AS percentoftotal
,CAST(ISNULL(SUM(c.PaidOriginal),0) AS DECIMAL(38,18)) / SUM(NULLIF(SUM(c.PaidOriginal),0) ) OVER (PARTITION BY c.calyr) AS percentoftotal
FROM c
db<>fiddle here

Changing data type to float and rounding to 2 decimal digits

Tables:
people(id, name)
job (id, people_id, job_title, salary)
Goal: Display each unique job, the total average salary (FLOAT and rounded to 2 decimal places), the total
people and the total salary (Float and rounded to 2 decimal places) and order by highest average salary.
So the challenge is to keep the cast type as float while rounding it to 2 decimal places.
I've gotten to where I've rounded it 2 decimal places but it's not float. I've gotten it to where it's float but I can't round it to 2 decimal places.
My Attempts:
Attempt 1:
SELECT
distinct(j.job_title) as job_title,
to_char(AVG(j.salary)::FLOAT, 'FM999999990.00') as average_salary,
COUNT(p.id) as total_people,
CAST (SUM(j.salary) AS FLOAT) as total_salary
FROM people p
JOIN job j on p.id = j.people_id
GROUP BY j.job_title
ORDER BY total_salary
Problem: Still says it's not float
Attempt 2:
SELECT
distinct(j.job_title) as job_title,
CAST (AVG(j.salary) AS FLOAT) as average_salary,
COUNT(p.id) as total_people,
CAST (SUM(j.salary) AS FLOAT) as total_salary
FROM people p
JOIN job j on p.id = j.people_id
GROUP BY j.job_title
ORDER BY total_salary
Problem: not rounded to 2 decimal places
Attempt 3:
SELECT
distinct(j.job_title) as job_title,
ROUND (AVG(CAST(j.salary as FLOAT)), 2)) as average_salary,
COUNT(p.id),
ROUND (SUM(CAST(j.salary as FLOAT)), 2)) as total_salary
FROM people p
JOIN job j on p.id = j.people_id
GROUP BY j.job_title
ORDER BY total_salary
I get an error saying I need to add explicit cast types which led me to attempt number 1.
The answer depends on the actual datatype of column salary. The key point is that round() in Postgres does not allows floats (only numeric types are supported).
If you are dealing with a numeric datatype, then you can first round(), then cast to float:
round(avg(salary), 2)::float
If you are dealing with a float column, then you would need to cast the result of the aggregate function before using round() on it:
round(avg(salary)::numeric, 2)::float
So the challenge is to keep the cast type as float while rounding it to 2 decimal places.
If you strictly want to avoid casting away from float, you could do it like this:
test=#
test=# SELECT float '12.3456'
test-# , round(float '12.3456' * 100) / 100 AS rounded
float8 | rounded
---------+----------
12.3456 | 12.35
The point being: round() taking the number of decimal places as 2nd parameter is based on numeric (since floating point numbers are imprecise by nature).
But there is an overloaded variant of round() taking a single parameter that rounds to the nearest integer. Multiply by 100 before and divide by 100 after.
Or overcome your aversion against numeric and use round(numeric, int) as provided by GMB.
Query
SELECT j.job_title -- !
, round(AVG(j.salary) * float '100') / 100 AS average_salary -- !
, COUNT(p.id) AS total_people
, SUM(j.salary)::float AS total_salary -- short cast syntax
FROM people p
JOIN job j ON p.id = j.people_id
GROUP BY j.job_title
ORDER BY total_salary DESC NULLS LAST -- ! "order by highest average salary"
Multiplying by * float '100' achieves the cast and the multiplication in one step - because the data type resulting from avg() depends on the input, quoting the manual:
numeric for any integer-type argument, double precision for a floating-point argument, otherwise the same as the argument data type
About casting in Postgres:
Postgres data type cast
distinct(j.job_title) is most probably not doing what you may think it does. There is no function distinct(). DISTINCT is a syntax element - and completely useless here, only adding cost. See this related case with explanation:
Skip whole row if aggregated value is null
About NULLS LAST:
PostgreSQL sort by datetime asc, null first?

SQL - 1. Round the difference to 2 decimal places

I am trying to create an SQL statement with a subquery in the SELECT attribute list to show the product id, the current price and the difference between the current price and the overall average.
I know that using the ROUND function will round the difference to zero decimals but I want to round the difference to 2 decimal places.
SELECT p_code, p_price, ROUND(p_price - (SELECT AVG(p_price) FROM product)) AS "Difference"
FROM product;
I tried using CAST but it still gave me the same output.
SELECT p_code, p_price, CAST(ROUND(p_price - (SELECT AVG(p_price) FROM Lab6_Product)) as numeric(10,2)) AS "Difference"
FROM lab6_product;
Thank you in advance for your time and help!
round() takes a second argument:
SELECT p_code, p_price,
ROUND(p_price - AVG(p_price) OVER (), 2) AS "Difference"
FROM product;
Note that I also changed the subquery to a window function.
I often recommend converting to a number or decimal/numeric) instead:
SELECT p_code, p_price,
cast(p_price - AVG(p_price) OVER () as number(10, 2)) AS "Difference"
FROM product;
This ensures that the two decimal points are displayed as well.

Trim a decimal to 2 places Bigquery

I am currently running a query that runs a sum function and also divides this number. Currently I get values like 0.0904246741698848, and 1.6419814808335567. I want these decimals to be trimmed to 2 spaces past the decimal point. Their schema is a float. Here is my code. Thanks for the help.
#standardSQL
SELECT
Serial,
MAX(createdAt) AS Latest_Use,
SUM(ConnectionTime/3600) as Total_Hours,
COUNT(DISTINCT DeviceID) AS Devices_Connected
FROM `dataworks-356fa.FirebaseArchive.Firebase_ConnectionInfo`
WHERE PeripheralType = 1 or PeripheralType = 2 or PeripheralType = 12
GROUP BY Serial
ORDER BY Latest_Use DESC
#standardSQL
WITH `data` AS (
SELECT 0.0904246741698848 AS val UNION ALL
SELECT 1.6419814808335567
)
SELECT val, ROUND(val, 2) AS rounded_val
FROM `data`
for example, assuming your want apply this to your Total_Hours column :
#standardSQL
SELECT
Serial,
MAX(createdAt) AS Latest_Use,
ROUND(SUM(ConnectionTime/3600),2) AS Total_Hours,
COUNT(DISTINCT DeviceID) AS Devices_Connected
FROM `dataworks-356fa.FirebaseArchive.Firebase_ConnectionInfo`
WHERE PeripheralType = 1 OR PeripheralType = 2 OR PeripheralType = 12
GROUP BY Serial
ORDER BY Latest_Use DESC
I found that rounding was problematic if my data had a whole number such as 2.00 and I needed all of my data to reflect 2 decimal places as these were for prices that end up getting displayed. Big Query was returning 2.0 no matter what I specified to round to using ROUND.
Assuming you're working with data that never surpasses 2 decimal places, and it is stored as a STRING, this code will work (if it's more decimal places, add another 0 to the addition for each space).
FORMAT("%.*f",2,CAST(GROSS_SALES_AMT AS FLOAT64) + .0001)
This will take a float in BigQuery and format it with two decimal points.
CAST(SUM(ConnectionTime/3600) AS STRING FORMAT '999,999.99')
Note: Add a a currency symbol (e.g., $) for currency ($999,999.99).
Example:
You can always use the round() function.
If you are looking for precision after decimal (as using round will round-off the values) you can use substr(str(value),precision) which will give exact output after decimal.

Calculate percentage between two columns in SQL Query as another column

I have a table with two columns, number of maximum number of places (capacity) and number of places available (availablePlaces)
I want to calculate the availablePlaces as a percentage of the capacity.
availablePlaces capacity
1 20
5 18
4 15
Desired Result:
availablePlaces capacity Percent
1 20 5.0
5 18 27.8
4 15 26.7
Any ideas of a SELECT SQL query that will allow me to do this?
Try this:
SELECT availablePlaces, capacity,
ROUND(availablePlaces * 100.0 / capacity, 1) AS Percent
FROM mytable
You have to multiply by 100.0 instead of 100, so as to avoid integer division. Also, you have to use ROUND to round to the first decimal digit.
Demo here
The following SQL query will do this for you:
SELECT availablePlaces, capacity, (availablePlaces/capacity) as Percent
from table_name;
Why not use a number formatting function such as format_number (or an equivalent one in your database) to format a double as a percentage? This example is generalized. The returned value is a string.
WITH t
AS
(
SELECT count(*) AS num_rows, count(foo) as num_foo
FROM mytable
)
SELECT *, format_number(num_foo/num_rows, '#.#%') AS pct_grade_rows
FROM t
This avoids the use of round and multiplying the numerator by 100.