Specifying a column value in an aggregate function vs using a WHERE clause - sql

I have a database people that looks like this:
I wanted to count the occurrences of state='CA'.
My first attempt was:
SELECT COUNT(state='CA')
FROM people
;
this returned 1 row with a value of 1000. So I thought that there were 1000 people from CA in the database.
This turns out to be incorrect. I know that they are 127, which I can verify with the query
SELECT COUNT(*)
FROM people
WHERE state='CA'
;
which returns 1 row with a value of 127.
I understand how the second query works. However, I do not understand what is wrong with the first one. What is it returning?

If you want to see what's going on, run the query:
select state='CA' from people;
You will see that you will get one result for each row in people, with the value 0 or 1 (or True/False). What you've selected is whether state='CA' for each row, and there will be just as many of those results as there are rows.
You can't constrain a COUNT statement within the statement, you have to do that via the WHERE clause as in your second example.

count is not a sum .. your first query is improper because don't return the number of the rows true .. but the total numbers of not null rows true or false
if you want a filter count you must use a where condition (as your second query) otherwise you must use an if or a a select case inside the sum() function eg:
Select sum(case
when state='CA' then 1 else 0
end) as my_result from People;
or if you want count .. use null and not 0min count
Select count(case
when state='CA' then 1 else null
end) as my_result from People;

Try this-:
Select count(case when state='CA' then 1 else null end) as xyz from People;

1st query will work if you use case when in side count,
like below query will returned count of CA
SELECT sum( case when state='CA' then 1 else 0 end)
FROM people

In first query it is assigning the value 'CA' to the column state for all 1000 rows instead of filtering the values. That is what SELECT does. SELECT does not filter the number of returning rows, it modifies the data.
Whereas in WHERE clause the rows are being filtered first then the SELECT clause runs the COUNT function.
There is a sequence for running the query. It starts from FROM then WHERE, GROUP BY, ORDER BY at the end SELECT will run.

To answer the actual question - why do you get 1000? I'm guessing that there are 1000 rows in your database, or at least 1000 where state is not null. Count will return the number of rows where the thing inside the () is not null and as one of your comments says, the part inside your () will return either true or false, neither of which is null, so will count them all. Your second example is of course the right way to do it.

Related

Why does a CASE WHEN give me a different result to a WHERE with the same condition?

I'm trying to use a case when and a pivot to filter some data, but get a result of a total count of 0, however, when I use the same condition in a where statement I get a result that's more what i expected.
SELECT * FROM(SELECT FYEAR,
CASE WHEN (DIAG_3_01 IN ('E10','E11','E12','E13','E14','O24') OR DIAG_4_01 IN ('E232','N251','P702')) AND (OPERTN_3_01 IN ('N26','P22','X07','X09','X10','X11') OR OPERTN_4_01 IN ('Q011','X215','X216','X273','X121')) THEN 'a'
ELSE 'Other' END AS 'Procs',
(FCE)
FROM database
) AS a
PIVOT(COUNT(FCE) FOR [Procs] IN ([a])) AS p;
So this results in a table with column name a and a row value of 0, whereas this code results in a total of about 4000:
SELECT COUNT(FCE)
FROM database
WHERE (DIAG_3_01 IN ('E10','E11','E12','E13','E14','O24') OR DIAG_4_01 IN ('E232','N251','P702')) AND (OPERTN_3_01 IN ('N26','P22','X07','X09','X10','X11') OR OPERTN_4_01 IN ('Q011','X215','X216','X273','X121'));
Unfortunately I cant share the database contents, but would appreciate any insight as to why this might be happening.
The CASE statement is like an If-statement, which only returns the very first value it founds that meets the criteria. You can also check multiple cases with WHEN and if no criteria are met, then the query will show whatever is in the ELSE statement.
The WHERE clause is just filtering the result, meaning it tell the query which records should not be ignored.

How to get 0 if no row found from sql query in sql server

I am getting blank value with this query from sql server
SELECT TOP 1 Amount from PaymentDetails WHERE Id = '5678'
it has no row,that is why its returning blank,So I want if no row then it should return 0
I already tried with COALESCE ,but its not working
how to solve this?
You are selecting an arbitrary amount, so one method is aggregation:
SELECT COALESCE(MAX(Amount), 0)
FROM PaymentDetails
WHERE Id = '5678';
Note that if id is a number, then don't use single quotes for the comparison.
To be honest, I would expect SUM() to be more useful than an arbitrary value:
SELECT COALESCE(SUM(Amount), 0)
FROM PaymentDetails
WHERE Id = '5678';
You can wrap the subquery in an ISNULL:
SELECT ISNULL((SELECT TOP 1 Amount from PaymentDetails WHERE Id = '5678' ORDER BY ????),0) AS Amount;
Don't forget to add a column (or columns) to your ORDER BY as otherwise you will get inconsistent results when more than one row has the same value for Id. If Id is unique, however, then remove both the TOP and ORDER BY as they aren't needed.
You should never, however, use TOP without an ORDER BY unless you are "happy" with inconsistent results.

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.)

How to display filtered rows in aggregation

Given the following query:
SELECT dbo.ClientSub.chk_Name as Details, COUNT(*) AS Counts
FROM dbo.ClientMain
INNER JOIN
dbo.ClientSub
ON dbo.ClientMain.ate_Id = dbo.ClientSub.ate_Id
WHERE chk_Status=1
GROUP BY dbo.ClientSub.chk_Name
I want to display the rows in the aggregation even if there are filtered in the WHERE clause.
NULL values are considered as zeros for aggregation purposes.
For your intention you should use GROUP BY ALL that returns rows which were filtered also, with zero as aggregated value.
in case you use oracle sql:
Have you tried Select nvl(dbo.ClientSub.chk_name,'0') .... ?
I hope you are using Oracle. You can directly use count(chk_name). Passing a column name in the count neglects null values. Your query above returns the total number of records against each chk_name group. When you use count(chk_name) it will count all the records where chk_name is not null.
I hope that answers your question.
Thanks,
Aditya
Do you mean this? Which returns count 0 if chk_Name is NULL.
SELECT
dbo.ClientSub.chk_Name as Details,
SUM(CASE WHEN ISNULL(dbo.ClientSub.chk_Name, '')<>'' then 1 else 0 end) AS Counts
FROM
dbo.ClientMain INNER JOIN dbo.ClientSub ON dbo.ClientMain.ate_Id = dbo.ClientSub.ate_Id
where chk_Status=1
group by dbo.ClientSub.chk_Name

Coalesce not evaluating second argument?

I am trying to run the following query:
SELECT COALESCE(count(percent_cov), 0)
FROM sample_cov
WHERE target = 542
GROUP BY percent_cov
HAVING percent_cov < 10
Basically, I want to show the number of times this statistic was < 10, and return 0 rather than null if the count was 0. If the count is >0 I get the number I want as the result, however if the count is 0 I still get a null returned. (Same thing if I set the second argument to coalesce as a positive number). What am I doing wrong?
I rewrote your query the way I think you want it:
SELECT count(*) AS ct
FROM sample_cov
WHERE target = 542
AND percent_cov < 10;
count() returns 0 When no matching rows (or non-null values in the column) are found. No need for coalesce(). I quote the manual on this:
It should be noted that except for count, these functions return a
null value when no rows are selected.
Bold emphasis mine. If you want to return a different value when count() comes back with 0, use a CASE statement.
Also, it's no use to write count(percent_cov) while you have WHERE percent_cov < 10. Only non-null values qualify, count(*) yields the same result slightly faster and simpler in this case.
You don't need a GROUP BY clause as you don't group by anything, you are aggregating over the whole table.
You could GROUP BY target, but this would be a different query:
SELECT target, count(*)
FROM sample_cov
WHERE percent_cov < 10
GROUP BY target;
You would need to spell out the expression in the HAVING clause again. Output column names are visible in ORDER BY and GROUP BY clauses, not in WHERE or HAVING.