Select sum and average from table with different condition - sql

I have a PostgreSQL table as shown in screenshot
I want to fetch the following data in same query
Sum of all units
Average of all unit_price only where units > 0
How can I achieve that?

Here is one possible approach (NOTE: I have no access to a PostgreSQL to check syntax, so I'm presenting here a sort of pseudo-code without checking it).
SELECT SUM(UNITS) AS UNITS_SUM ,
SUM(UNIT_PRICE)/POSS_UNITS_COUNT AS AVERAGE_UNIT_PRICE
FROM
(SELECT UNITS AS UNITS ,
CASE WHEN UNITS > 0 THEN 1
ELSE 0
END AS POSS_UNITS_COUNT ,
CASE WHEN UNITS > 0 THEN UNIT_PRICE
ELSE 0
END AS UNIT_PRICE
FROM Your_Table) A ;
Note that this would yield a DIVIDE BY 0 error if no positive price is in your table (I'm assuming that such condition cannot exist; if it does, you should condition the division using a CASE where POSS_UNITS_COUNT must be > 0.

I would just use conditional aggregation. In Postgres, that uses filter:
select sum(units),
avg(unit_price) filter (where units > 0)
from t;

Related

How to get count(percentage) for columns after each groupby item?

I have the following table. Using sqlite DB
Item
Result
A
Pass
B
Pass
A
Fail
B
Fail
I want to realize the above table as below using some query.
Item
Total
Accept
Reject
A
2
1(50%)
1(50%)
B
2
1(50%)
1(50%)
How should I construct this query?
You can try PIVOT() if your DBMS supports. Then use CONCAT or || operator depending on the DMBS.
Query:
SELECT
item,
total,
SUM(Pass)||'('|| CAST((SUM(Pass)*1.0/total*1.0)*100.0 AS DECIMAL)||'%)' AS Accept,
SUM(Fail)||'('|| CAST((SUM(Fail)*1.0/total*1.0)*100.0 AS DECIMAL)||'%)' AS Reject
FROM
(
SELECT
Item,
result,
COUNT(result) OVER(PARTITION BY item ORDER BY result ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS total,
CASE
WHEN Result = 'Pass' then 1
ELSE 0
END AS Pass,
CASE
WHEN Result = 'Fail' then 1
ELSE 0
END AS Fail
FROM t
) AS j
GROUP BY item, total
Query explanation:
Since SQLITE does not handle PIVOT, we are creating the flags Pass and Fail manually using CASE statement
To calculate total, COUNT is used as analytical function here. It is basically a shortcut to calculate count and place it in all rows
Then in the outer query, we are calculating %s and using || as the concatenate operator to concatenate the result with total sum and % of it
See demo in db<>fiddle

SQL selecting with conditioning from a subquery

I am trying to perform two sum functions from a query. However, I want to only perform one of the sum functions if it meets a certain condition without affecting the other sum function.
What I was thinking is to use something similar to select x where condition = 1 from AC which is however not possible.
Here is the sample query where I want the second [sum(t.match)] selection to only calculate if the result in the subquery: match = 1 while still getting the total sum of all qqty.
select
sum(t.qqty), sum(t.qqty)
from
(select
car, cqty, qqty,
case when cqty = qqty then 1 else 0 end as match,
location, state) t
Use conditional aggregation -- that is case as the argument to the sum():
select sum(t.qqty), sum(case when condition = 1 then t.qqty else 0 end)
from t;

PostgreSQL : Percentage without a WHERE

i need to get a percentage but can't use the WHERE clause because it is a part of a large SQL query.
I try to do this :
select (count(sector='Rurality'))/(count(sector))*100 as test from study
But the first count get full results instead of filtering.
In other words, this doesn't work :
select COUNT(sector='Rurality') AS test FROM study;
Maybe somebody could have any idea ? The problem is that filters are glued to the SQL query after all of this but can't add a WHERE sector="rurality".
This is what FILTER is for:
select count(*) filter (where sector = 'Rurality') test from study;
For older PostgreSQL, you can use the CASE construct, but don't forget to omit the ELSE clause to not count NULL values:
select count(case sector when 'Rurality' then 1 end) test from study;
Also, bigint / bigint will be bigint, so use casts and/or parenthesis, or just re-structure your formula, like:
select 100.0 * count(*) filter (where sector = 'Rurality') / count(sector) test
from study;
Your approach works with sum():
select sum((sector='Rurality')::int)::dec / count(sector)*100 as test from study
Use a CASE statement inside the COUNT.
SELECT (COUNT(CASE WHEN sector = 'Rurality' THEN 1 END)) / (COUNT(sector)) * 100 AS test
FROM study

sqlite3 join query from single table

A sqlite3 db table contains device perf data with two columns.. device and value.
Content is something like this
deviceA|50
deviceB|75
deviceA|125
deviceB|25
deviceA|99
deviceB|10
deviceA|101
and on and on
For each device
I want to know how many entries are in the table (total)
I want to know how mnay entries are over threshold of 100 (overthreshold)
I want to know how many entries are under threshold of 100 (underthreshold)
I want to know the percent of total entries under threshold (percent)
This is my query so far
select distinct(total.device),
total.count,
overthreshold.count,
round(((total.count*1.0 - overthreshold.count)/total.count),4)*100
from
(select device,count(*) as count from perfdata group by device) as total
inner join (
select device,count(*) as count from perfdata where value>100 group by device
) as overthreshold
group by overthreshold.device;
deviceA only results included here
deviceA|2017|16|99.21
deviceA had 2017 entries in the table, 16 of which are > 100; 99.21% under threshold.
for all device/value combinations, output currently only shows those overthreshold as my query tells it to.
deviceB is never overthreshold and isn't in query output (100% under threshold).
Any advice on where/how would I add in the
select device,count(*) as count from perfdata where value<100 group by device
statement to get underthreshold returned back for inclusion in my calculation?
Thanks for any help.
You want to use conditional aggregation. This is where you use the case statement along with the aggregation functions:
select device, count(*) as TotalCount,
sum(case when value > 100 then 1 else 0 end) as OverThreshhold,
sum(case when value < 100 then 1 else 0 end) as UnderThreshhold,
100.0 * avg(case when value < 100 then 1.0 else 0.0 end) as PercentageUnder
from perfdata
group by device;

Multiple aggregate functions in query

I need to have two aggregate functions in my query, but can't figure out how to filter.
I need the number of samples and the number of samples greater than 1.
something like:
SELECT COUNT(Samples), COUNT(Samples >1)
FROM SampleData
I could do a subquery, but is there a better way to filter like this?
You can basically then the value of Sample using CASE and the result of it is the aggregated via SUM().
SELECT COUNT(Samples),
SUM(CASE WHEN Samples > 1 THEN 1 ELSE 0 END)
FROM SampleData
This will work on most RDBMS though.
To get the number of records, see JW's answer. Similarly, to get the total value of samples, and the total value of samples where samples>1, use:
SELECT SUM(Samples) TotalSamples,
SUM(CASE WHEN Samples > 1 THEN Samples ELSE 0 END) SamplesGT1
FROM SampleData
In case you're using PostgreSQL, you can use the standard SQL FILTER clause:
SELECT COUNT(*), COUNT(*) FILTER (WHERE Samples > 1)
FROM SampleData