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
Related
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
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.)
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
Let's say you want do something along the following lines:
SELECT CASE
WHEN (SELECT COUNT(id) FROM table WHERE column2 = 4) > 0
THEN 1 ELSE 0 END
Basically just return 1 when there's one or more rows in the table, 0 otherwise. There has to be a grammatically correct way to do this. What might it be? Thanks!
Question: return 1 when there's one or more rows in the table, 0 otherwise:
In this case, there is no need for COUNT. Instead, use EXISTS, which rather than counting all records will return as soon as any is found, which performs much better:
SELECT CASE
WHEN EXISTS (SELECT 1 FROM table WHERE column2 = 4)
THEN 1
ELSE 0
END
Mahmoud Gammal posted an answer with an interesting approach. Unfortunately the answer was deleted due to the fact that it returned the count of records instead of just 1. This can be fixed using the sign function, leading to this more compact solution:
SELECT sign(count(*)) FROM table WHERE column2 = 4
I posted this because I find it an interesting approach. In production I'd usually end up with something close to RedFilter's answer.
You could do this:
SELECT CASE WHEN COUNT(ID) >=1 THEN 1 WHEN COUNT (ID) <1 THEN 0 END FROM table WHERE Column2=4
Reference:
http://msdn.microsoft.com/en-us/library/ms181765.aspx
I need to calculate the net total of a column-- sounds simple. The problem is that some of the values should be negative, as are marked in a separate column. For example, the table below would yield a result of (4+3-5+2-2 = 2). I've tried doing this with subqueries in the select clause, but it seems unnecessarily complex and difficult to expand when I start adding in analysis for other parts of my table. Any help is much appreciated!
Sign Value
Pos 4
Pos 3
Neg 5
Pos 2
Neg 2
Using a CASE statement should work in most versions of sql:
SELECT SUM( CASE
WHEN t.Sign = 'Pos' THEN t.Value
ELSE t.Value * -1
END
) AS Total
FROM YourTable AS t
Try this:
SELECT SUM(IF(sign = 'Pos', Value, Value * (-1))) as total FROM table
I am adding rows from a single field in a table based on values from another field in the same table using oracle 11g as database and sql developer as user interface.
This works:
SELECT COUNTRY_ID, SUM(
CASE
WHEN ACCOUNT IN 'PTBI' THEN AMOUNT
WHEN ACCOUNT IN 'MLS_ENT' THEN AMOUNT
WHEN ACCOUNT IN 'VAL_ALLOW' THEN AMOUNT
WHEN ACCOUNT IN 'RSC_DEV' THEN AMOUNT * -1
END) AS TI
FROM SAMP_TAX_F4
GROUP BY COUNTRY_ID;
select a= sum(Value) where Sign like 'pos'
select b = sum(Value) where Signe like 'neg'
select total = a-b
this is abit sql-agnostic, since you didnt say which db you are using, but it should be easy to adapat it to any db out there.