Multiple aggregate functions in query - sql

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

Related

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;

Select sum and average from table with different condition

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;

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

Return NULL instead of 0 when using COUNT(column) SQL Server

I have query which running fine and its doing two types of work, COUNT and SUM.
Something like
select
id,
Count (contracts) as countcontracts,
count(something1),
count(something1),
count(something1),
sum(cost) as sumCost
from
table
group by
id
My problem is: if there is no contract for a given ID, it will return 0 for COUNT and Null for SUM. I want to see null instead of 0
I was thinking about case when Count (contracts) = 0 then null else Count (contracts) end but I don't want to do it this way because I have more than 12 count positions in query and its prepossessing big amount of records so I think it may slow down query performance.
Is there any other ways to replace 0 with NULL?
Try this:
select NULLIF ( Count(something) , 0)
Here are three methods:
1. (case when count(contracts) > 0 then count(contracts) end) as countcontracts
2. sum(case when contracts is not null then 1 end) as countcontracts
3. nullif(count(contracts), 0)
All three of these require writing more complicated expressions. However, this really isn't that difficult. Just copy the line multiple times, and change the name of the variable on each one. Or, take the current query, put it into a spreadsheet and use spreadsheet functions to make the transformation. Then copy the function down. (Spreadsheets are really good code generators for repeated lines of code.)

Sql Server equivalent of a COUNTIF aggregate function

I'm building a query with a GROUP BY clause that needs the ability to count records based only on a certain condition (e.g. count only records where a certain column value is equal to 1).
SELECT UID,
COUNT(UID) AS TotalRecords,
SUM(ContractDollars) AS ContractDollars,
(COUNTIF(MyColumn, 1) / COUNT(UID) * 100) -- Get the average of all records that are 1
FROM dbo.AD_CurrentView
GROUP BY UID
HAVING SUM(ContractDollars) >= 500000
The COUNTIF() line obviously fails since there is no native SQL function called COUNTIF, but the idea here is to determine the percentage of all rows that have the value '1' for MyColumn.
Any thoughts on how to properly implement this in a MS SQL 2005 environment?
You could use a SUM (not COUNT!) combined with a CASE statement, like this:
SELECT SUM(CASE WHEN myColumn=1 THEN 1 ELSE 0 END)
FROM AD_CurrentView
Note: in my own test NULLs were not an issue, though this can be environment dependent. You could handle nulls such as:
SELECT SUM(CASE WHEN ISNULL(myColumn,0)=1 THEN 1 ELSE 0 END)
FROM AD_CurrentView
I usually do what Josh recommended, but brainstormed and tested a slightly hokey alternative that I felt like sharing.
You can take advantage of the fact that COUNT(ColumnName) doesn't count NULLs, and use something like this:
SELECT COUNT(NULLIF(0, myColumn))
FROM AD_CurrentView
NULLIF - returns NULL if the two passed in values are the same.
Advantage: Expresses your intent to COUNT rows instead of having the SUM() notation.
Disadvantage: Not as clear how it is working ("magic" is usually bad).
I would use this syntax. It achives the same as Josh and Chris's suggestions, but with the advantage it is ANSI complient and not tied to a particular database vendor.
select count(case when myColumn = 1 then 1 else null end)
from AD_CurrentView
How about
SELECT id, COUNT(IF status=42 THEN 1 ENDIF) AS cnt
FROM table
GROUP BY table
Shorter than CASE :)
Works because COUNT() doesn't count null values, and IF/CASE return null when condition is not met and there is no ELSE.
I think it's better than using SUM().
Adding on to Josh's answer,
SELECT COUNT(CASE WHEN myColumn=1 THEN AD_CurrentView.PrimaryKeyColumn ELSE NULL END)
FROM AD_CurrentView
Worked well for me (in SQL Server 2012) without changing the 'count' to a 'sum' and the same logic is portable to other 'conditional aggregates'. E.g., summing based on a condition:
SELECT SUM(CASE WHEN myColumn=1 THEN AD_CurrentView.NumberColumn ELSE 0 END)
FROM AD_CurrentView
It's 2022 and latest SQL Server still doesn't have COUNTIF (along with regex!). Here's what I use:
-- Count if MyColumn = 42
SELECT SUM(IIF(MyColumn = 42, 1, 0))
FROM MyTable
IIF is a shortcut for CASE WHEN MyColumn = 42 THEN 1 ELSE 0 END.
Not product-specific, but the SQL standard provides
SELECT COUNT() FILTER WHERE <condition-1>,
COUNT() FILTER WHERE <condition-2>, ...
FROM ...
for this purpose. Or something that closely resembles it, I don't know off the top of my hat.
And of course vendors will prefer to stick with their proprietary solutions.
Why not like this?
SELECT count(1)
FROM AD_CurrentView
WHERE myColumn=1
I had to use COUNTIF() in my case as part of my SELECT columns AND to mimic a % of the number of times each item appeared in my results.
So I used this...
SELECT COL1, COL2, ... ETC
(1 / SELECT a.vcount
FROM (SELECT vm2.visit_id, count(*) AS vcount
FROM dbo.visitmanifests AS vm2
WHERE vm2.inactive = 0 AND vm2.visit_id = vm.Visit_ID
GROUP BY vm2.visit_id) AS a)) AS [No of Visits],
COL xyz
FROM etc etc
Of course you will need to format the result according to your display requirements.
SELECT COALESCE(IF(myColumn = 1,COUNT(DISTINCT NumberColumn),NULL),0) column1,
COALESCE(CASE WHEN myColumn = 1 THEN COUNT(DISTINCT NumberColumn) ELSE NULL END,0) AS column2
FROM AD_CurrentView