Calculate average for specific values - sql

I'd like to calculate the average of a column but I'm getting stuck because, if a value is within a certain range than only I'd like to include that.
So if I had the following values:
100, 150, 500, 450, 300, 750
Now, I want to include only the values which is greater than 400 as a part of average.
then the query would calculate the average of these values:
500, 450, 750
and the output will be (500+450+750)/3
I tried
1)
select avg(case when value > 400 then value else 0 end) avg_val
from test
This is giving output as (0+0+500+450+0+750)/6. This is what not I wanted!
2)
select SUM(case when value > 400 then value else 0 end) /
SUM(case when value > 400 then 1 else 0 end) avg_val
from test
this is throwing error saying divide by zero if there is no value greater than 400.
Can anybody help ? I am using PostgreSQL.
Thanks!

Well, AVG() doesn't calculate null values, so you can use your query and replace 0 with NULL:
select avg(case when value > 400 then value else null end) avg_val
from test
Which can be formatted without the else part as well, since the default of the else is NULL
select avg(case when value > 400 then value end) avg_val
from test

Simply:
select avg(value)
from test
where value > 400

SELECT AVG(Price) AS PriceAverage FROM Products where price between 10 and 30;
SELECT AVG(Price) AS PriceAverage FROM bidding where price >=2500 ;

Related

Calculate rate based on condition using postgresql

I want to find the rate of negative and zero profits from a column. I tried to do it using aggregate and subquery but it doesn't seem to work as both method return 0 values.
The code is as follows
SELECT
COUNT(CASE WHEN profit < 0 THEN 1
END) AS negative_profits,
COUNT(CASE WHEN profit < 0 THEN 1
END) / COUNT(profit),
COUNT(CASE WHEN profit = 0 THEN 1
END) AS zero_profits,
COUNT(CASE WHEN profit = 0 THEN 1
END) / COUNT(profit)
FROM sales;
SELECT (SELECT COUNT(*)
FROM sales
WHERE profit <= 0)/COUNT(profit) AS n_negative_profit
FROM sales;
Both query return 0 in values
enter image description here
Avoid integer division, which truncates (like Adrian pointed out).
Also, simplify with an aggregate FILTER expression:
SELECT count(*) FILTER (WHERE profit <= 0)::float8
/ count(profit) AS n_negative_profit
FROM sales;
If profit is defined NOT NULL, or to divide by the total count either way, optimize further:
SELECT count(*) FILTER (WHERE profit <= 0)::float8
/ count(*) AS n_negative_profit
FROM sales;
See:
Aggregate columns with additional (distinct) filters
Because you are doing integer division per docs Math operators/functions.
numeric_type / numeric_type → numeric_type
Division (for integral types, division truncates the result towards zero)
So:
select 2/5;
0
You need to make one of the numbers float or numeric:
select 2/5::numeric;
0.40000000000000000000
and to make it cleaner round:
select round(2/5::numeric, 2);
0.40

SQL using SUM in CASE in SUM

I had this query select
sum(CASE WHEN kpi.average >= temp.average THEN 1 ELSE 0 END) AS recordOrder,
which worked fine, but I had to change it to this
sum(CASE WHEN sum(kpi.averageUse) / sum(kpi.averageTotal) >= temp.average THEN 1 ELSE 0 END) AS recordOrder,
These queries have to get number of rows, where some value (average) is greater than average from TEMP table. But in the second query I have more accurate data (weighted average).
but I am getting error
1111 invalid use of group function
Any ideas how to write SUM in CASE in SUM?
Thanks!
This code is just non-sensical because you have nested sum functions:
sum(CASE WHEN sum(kpi.averageUse) / sum(kpi.averageTotal) >= temp.average THEN 1 ELSE 0 END) AS recordOrder,
Without seeing your larger query, it is not possible to know what you really intend. But I would speculate that you don't want the internal sum()s:
sum(CASE WHEN (skpi.averageUse / kpi.averageTotal) >= temp.average THEN 1 ELSE 0 END) AS recordOrder,

Best way to find number of account within certain value ranges

I am having trouble what would be the best/most efficient way to find all accounts that fall within a certain value range without having 20 different selects.
So for example I need to find the following:
Account Value Number of plans average value
$100-$10000
$11000-$15000
$16000-$20000
$21000-$30000
$30000+
So right now my query is basically this for every value range:
SELECT COUNT (acct) AS 'Number of Plans'
, AVG (Value) AS 'Average Value'
FROM #RT1
WHERE Value
BETWEEN 0 AND 249999.99
AND InstaCode = 'S'
and there are three different charts that needs to be populated in SSRS. The only way I can figure it out is writing 15 different select statements but i feel there should be an easier and more effective way to do this.
Thanks!
I like to use cross apply for this:
SELECT v.grp, COUNT(acct) AS num_plans, AVG(value) as avg_value
FROM #RT1 t CROSS APPLY
(VALUES (CASE WHEN value >= 100 and value < 10000 THEN '$100-$10000'
WHEN value < 15000 THEN '$11000-$15000'
WHEN value < 20000 THEN '$16000-$20000'
WHEN value < 30000 THEN '$21000-$30000'
ELSE '$30000+'
END) as grp
) v(grp)
GROUP BY v.grp;
I'm not sure what InstaCode = 'S' has to do with the results. It is easy enough to add, either to the CASE expression or to a WHERE clause.
Use conditional aggregation for each group:
SELECT COUNT (case when Value
BETWEEN 0 AND 249999.99
then value else null end) AS 'Number of Plans group 1',
COUNT (case when Value
BETWEEN 2500000 AND 3000000
then value else null end) AS 'Number of Plans group 2',
AVG (case when Value
BETWEEN 0 AND 249999.99
then value else null end) AS 'Average Value 1st group', AVG (case when Value
BETWEEN 2500000 AND 3000000
then value else null end) AS 'Average Value 2nd group'...
from #RT1
where instacode='s'
SELECT
Case
When Value between 100 and 10000 Then '100 to 10000'
When Value between 11000 and 15000 Then '11000 to 15000'
When Value between 16000 and 20000 Then '16000 to 20000'
When Value between 21000 and 30000 Then '21000 to 30000'
When Value > 30000 Then '30000+'
End as AccountValue
COUNT (acct) AS NumberofPlans
, AVG (Value) AS AverageValue
FROM #RT1
WHERE Value
BETWEEN 0 AND 249999.99
AND InstaCode = 'S'
Group by
Case
When Value between 100 and 10000 Then '100 to 10000'
When Value between 11000 and 15000 Then '11000 to 15000'
When Value between 16000 and 20000 Then '16000 to 20000'
When Value between 21000 and 30000 Then '21000 to 30000'
When Value > 30000 Then '30000+'
End

SQL, querying sum of positive results, absolute value

I have the following query which returns a total dollar amount.
select sum(cast(dollars as dec)) from financials
This includes positive and negative values.
I would like 2 separate things:
How can I just query the positive dollar amounts? ie. I have 3 records, 10 , -5 , 10. result would be 20.
I want an absolute value as a sum. ie. I have 3 records, 10, -5, 10. the result would be 25.
thanks.
FOR 1) Use conditional SUM()
SELECT SUM( CASE WHEN dollars > 0 then dollars ELSE 0 END) as positive_sum,
SUM( CASE WHEN dollars < 0 then dollars ELSE 0 END) as negative_sum
FROM financials
FOR 2) use ABS()
SELECT SUM( ABS( dollars ) )
FROM financials
Please try below queries. Thanks.
1) select sum(cast(dollars as dec))
from financials
where dollars > 0;
2) select sum(cast(abs(dollars) as dec))
from financials;
You have two queries.solutions are as follows
1.
select sum(dollars) from financials
2.
select sum((case when dollars>0 then dollars end))+sum((case when dollars<0 then -1*dollars end)) from financials

SQL Query to sum buckets

I have a table with inovices and a field named Arrers days and I need to count how many items are into 0-30 days also 30-90 and 90-120
Basically what I need is to calcuate the aging of my client portfolio.
so far I have this:
SELECT SUM(CASE WHEN Receivable.Arrers>'0'<'30' THEN 1 ELSE 0) AS 0-30,
SUM(CASE WHEN Receivable.Arrers<'30'>'60' THEN 1 ELSE 0) AS 30-60,
SUM(CASE WHEN Receivable.Arrers<'90'>'120' THEN 1 ELSE 0) AS 90-120
From Receivable
Table Name Receivable
Invoice Arrers
89859 10
89856 3
89853 11
Access SQL does not support CASE WHEN. You can use an IIf expression instead.
SELECT
SUM(IIf(r.Arrers BETWEEN 0 AND 30, 1, 0)) AS [0-30],
SUM(IIf(r.Arrers BETWEEN 31 AND 60, 1, 0)) AS [31-60],
SUM(IIf(r.Arrers BETWEEN 90 AND 120, 1, 0)) AS [90-120]
FROM Receivable AS r;
The example in your question ignores Arrers from 61 to 89, so I did, too. But you can add in another column expression if needed.
If Arrers is text instead of numeric datatype, you can use Val() to cast the text values to numbers ...
SUM(IIf(Val(r.Arrers) BETWEEN 0 AND 30, 1, 0)) AS [0-30]
You can try that one
SELECT
SUM( CASE WHEN Receivable.Arrers BETWEEN 0 AND 30 THEN 1 ELSE 0) AS '0-30',
SUM (CASE WHEN Receivable.Arrers BETWEEN 30 AND 60 THEN 1 ELSE 0) AS '30-60',
SUM (CASE WHEN Receivable.Arrers BETWEEN 90 AND 120 THEN 1 ELSE 0) AS '90-120',
arrers
FROM Receivable
GROUP BY arrers
1 thing need to be checked.
I've put BETWEEN 90 AND 120 as I consider it integer. If they are varchar, you will need to turn them into integers. Val(Receivable.Arrers) will do the trick on access. Please let me know if there is anything unclear
You are on the right track except possibly for the CASE stmt syntax .... try Andy's answer without the Group by
(Dont have enough points to post as a comment)
I'd use something like this:
SELECT subq.ArrersGroup, COUNT(*) 'Count'
FROM
(
SELECT
CASE
WHEN R.Arrers < 30 THEN '0-30'
WHEN R.Arrers < 90 THEN '30-90'
WHEN R.Arrers < 120 THEN '90-120'
ELSE 'Other'
END ArrersGroup
FROM Receivable R
) subq
GROUP BY subq.ArrersGroup
SqlFiddle