Issue with case when SQL - sql

I have a table named 'candidate' which contains among others columns ,score_math' and 'score_language' reflecting candidate's score in respective tests. I need to
Show the number of students who scored at least 60 in both math and language (versatile_candidates) and the number of students who scored below 40 in both of
these tests (poor_candidates). Don't include students with NULL preferred_contact. My query is:
select
count(case when score_math>=60 and score_language>=60 then 1 else 0
end) as versatile_candidates,
count(case when score_math<40 and score_language<40 then 1 else 0 end) as
poor_candidates
from candidate
where preferred_contact is not null
But this produces always total number of candidates wit not-null preferred contact type. Can't really figure out what I did wrong and more importantly why this doesn't work. [DBMS is Postgres if this matters ]Please help

You're close - the reason you're getting the total number of all candidates is because COUNT() will count a 0 the same as a 1 (and any other non-NULL value, for that matter). And since the values could only ever be 0 or 1, your COUNT() will return the total number of all candidates.
Since you're already defaulting the cases that don't match to 0, all you need to do is change the COUNT() to a SUM():
Select Sum(Case When score_math >= 60
And score_language >= 60 Then 1
Else 0
End) As versatile_candidates
, Sum(Case When score_math < 40
And score_language < 40 Then 1
Else 0
End) As poor_candidates
From candidate
Where preferred_contact Is Not Null

COUNT() does not take into consideration NULL values. All other values which are not NULL will be counted.
You might want to replace it with SUM()

Related

Count() Specifying Uncounted Value?

Using Microsoft SQL Server, if you use COUNT(column name) it returns the number of rows in that column which have a non-null value (i.e., it counts the rows, ignoring nulls).
Is there any way to do something similar, but allowing you to tell it which values to ignore? For example, if I wanted to count all the rows in a table which have a value which is NOT 1, I could do something like COUNTNOT(column name,1). That would count all the rows in the specified column which have a value NOT 1.
You may use conditional aggregation:
SELECT COUNT(CASE WHEN some_val <> 1 THEN 1 END) AS cnt
FROM yourTable;
The above logic is that COUNT will count one whenever some value is not equal to 1. Otherwise, it falls on the ELSE conditional, which if not present defaults to the value NULL. Since NULL is not counted, any value other than 1 would contribute zero to the count.
Why not put what you want to exclude in a WHERE clause?
SELECT COUNT(some_val) AS cnt
FROM yourTable
WHERE some_val <> 1
You need to be careful about NULL values. I would recommend:
select sum(case when column in (<values to ignore>) then 0 else 1 end)
This will count NULL values as not in the list (even if NULL is in the list). To ignore NULL values (as well), switch the logic to:
select sum(case when column not in (<values to ignore>) then 1 else 0 end)
and be sure NULL is not in the list.

Count number of + and - values based on another column

I am trying to get a simple count of all the negative and positive values of a specific column on my database. I want to be able to count these values based on another column. What would be the best way to go about handling this problem.
enter image description here
Something like this:
SELECT
SUM(CASE WHEN column > 0 THEN 1 ELSE 0 END) as count_positive,
SUM(CASE WHEN column < 0 THEN 1 ELSE 0 END) as count_negative
FROM
table
You must put the column name, table name, and decide whether 0 is positive, negative or excluded (my choice)

Change the value of a sum in sql

I'm doing a query to obtain the numbers of people for a Christmas dinner.
The people include the workers and their relatives. The relatives are stored in a different table.
Children and adults eat a different menu and we organize tables by families.
I'm already using this query
select worker_name,
count(*) as total_per_family,
SUM(CASE WHEN age < 18 THEN 1 ELSE 0 END) as children,
SUM(CASE WHEN age >= 18 THEN 1 ELSE 0 END) as adults
from
(
/*subquery*/
)
group by worker_name
order by worker_name;
This query returns the number of child and adults related to the worker and count gives me the total.
The problem is that I need to add the worker to the adults sum.
Is there a way to modify adults? Either setting its initial value to 1 or adding 1 after the sum is done but before the count is obtained.
Modifying your query to read
SUM(CASE WHEN AGE>=18 THEN 1 ELSE 0 END) + 1 as adults
would probably be a first approach. The aggregate SUM() would be computed first, with 1 added thereafter as your initial suggestion indicated.

Divide by zero error encountered even when divider is set as NOT NULL

I've come across the error "Divide by zero error encountered" when running this query.
> SUM(CASE WHEN EML_DateSent IS NOT NULL THEN 1 ELSE 0 END) AS [Sends],
(SUM(CASE WHEN EML_DateViewed IS NOT NULL OR EML_DateClicked IS NOT NULL THEN 1 ELSE 0 END)) * 100 / SUM((CASE WHEN EML_Datesent IS NOT NULL THEN 1 ELSE 0 END)) AS [Views %],
(SUM(CASE WHEN EML_DateClicked IS NOT NULL THEN 1 ELSE 0 END)) * 100 / SUM((CASE WHEN EML_DateViewed IS NOT NULL OR EML_DateClicked IS NOT NULL THEN 1 ELSE 0 END)) AS [Clicks %]
Its an edited existing stored procedure that now calculates percentages , any quick fix ?
Try using a max/maxium statement depending on what provider you are using.
/ MAX(SUM((CASE WHEN EML_DateViewed IS NOT NULL OR EML_DateClicked IS NOT NULL THEN 1 ELSE 0 END)), 1)
This will use your sum if it has a value, if it is zero the division will use 1 instead.
You don't show the grouping criteria but it's obvious at least one of the groups has one of the dates set to NULL over the entire group. First, you don't have to put all that logic in a sum function. The count function does that, counting all the not null values, ignoring the null values. Where that doesn't work is where you're checking both dates, but that is solved by a simple coalesce. You want a count of where one date or the other or both are not null. There you can play a little trick:
select count(EMS_DateSent) AS Sends,
count(coalesce(EMS_DateViewed, EMS_DateClicked)) * 100
/ case count(EMS_Datesent)
when 0 then 1000000
else count(EMS_Datesent)
end as "Views %",
count(EMS_DateClicked) * 100
/ case count(coalesce(EMS_DateViewed, EMS_DateClicked))
when 0 then 1000000
else count(coalesce(EMS_DateViewed, EMS_DateClicked))
end AS "Clicks %"
from EML
group by whatever;
If the divisor is 0 (all nulls over the group), I have set to a large number so you get a very small answer. But this must be large relative to actual counts in your application so adjust as needed.

Counting non-zero values in sql

I am trying to count total number of times that each individual column is greater than zero, grouped by the driver name. Right now I have;
SELECT drivername
, COUNT(over_rpm) AS RPMViolations
, COUNT(over_spd) AS SpdViolations
, COUNT(brake_events) AS BrakeEvents
FROM performxbydriverdata
WHERE over_rpm > 0
OR over_spd > 0
OR brake_events > 0
GROUP BY drivername
This gives me all of the non-zero values but I get a display as:
Bob Smith 62 62 62
Nathan Jones 65 65 65
etc.
I'm trying to get a count of non-zeros in each individual values.. each violation should be grouped separately.
Use NULLIF to change zero to NULL, count ignores NULL
SELECT drivername,
COUNT(NULLIF(over_rpm,0)) AS RPMViolations,
COUNT(NULLIF(over_spd,0)) AS SpdViolations,
COUNT(NULLIF(brake_events,0)) AS BrakeEvents
FROM performxbydriverdata
GROUP BY drivername;
You can probably remove the WHERE clause too with this group to improve performance
OR conditions often run badly because of matching a good index
Using HAVING (as per other answers) will remove any rows where all 3 aggregates are zero which may or may not be useful for you. You can add this if you want. Saying that, the WHERE implies that at least one row has non-zero values so you don't need both WHERE and HAVING clauses
Putting filter predicate[s] inside of a Sum() function with a case statement is a useful trick anytime you need to count items based on some predicate condition.
Select DriverName,
Sum(case When over_rpm > 0 Then 1 Else 0 End) OverRpm,
Sum(case When over_spd > 0 Then 1 Else 0 End) OverSpeed,
Sum(case When brake_events > 0 Then 1 Else 0 End) BrakeEvents,
etc.
FROM performxbydriverdata
Group By DriverName