posgresql exclude zero division error results - sql

I am executing a query but for sum cases
SUM( CASE WHEN dismissal_kind = 'caught' THEN 1 ELSE 0 END )
this part of the code results into zero which causes zero division error.I am trying to not show result(not select) when
SUM( CASE WHEN dismissal_kind = 'caught' THEN 1 ELSE 0 END )
Pease help.
"""select distinct bowler as b,
count(bowler)/SUM( CASE WHEN dismissal_kind = 'caught' THEN 1 ELSE 0 END ) from deliveries
group by bowler; """

You can do it this way, by using case when:
In my example i put the default value to 0 but you can have a default value different depending on your use case
select distinct bowler as b,
case when SUM( CASE WHEN dismissal_kind = 'caught' THEN 1 ELSE 0 END ) <> 0
then
count(bowler)/SUM( CASE WHEN dismissal_kind = 'caught' THEN 1 ELSE 0 END )
else
0
end
from deliveries
group by bowler;

The simplest way is to remove the else 0:
SUM( CASE WHEN dismissal_kind = 'caught' THEN 1 END )
This returns NULL if nothing matches the condition. Dividing by NULL produces NULL and not an error.
For your query however, I recommend:
select bowler as b,
count(*) / nullif(count(*) filter (where dismissal_kind = 'caught'), 0)
from deliveries
group by bowler;
nullif() is another way to avoid division by zero. filter is the recommended (and standard) syntax for conditional aggregation.
If you want to filter out the rows that are null, you can include a having clause:
select bowler as b,
count(*) / nullif(count(*) filter (where dismissal_kind = 'caught'), 0)
from deliveries
group by bowler
having count(*) filter (where dismissal_kind = 'caught') > 0;

Related

SQL - CASE WHEN result outputting conflicting results

I'm working to solve
https://platform.stratascratch.com/coding/10065-find-whether-the-number-of-seniors-works-at-facebook-is-higher-than-its-number-of-usa-based-employees?python=
This is the query I've attempted to write:
SELECT CASE WHEN COUNT(CASE WHEN location = 'US' THEN 1 ELSE 0 END) >
COUNT(CASE WHEN is_senior = true THEN 1 ELSE 0 END) THEN 'More USA-based'
ELSE 'More seniors' END AS what_do_we_have_more_of
FROM facebook_employees
Result: 'More seniors'
However, when I rewrite it with the conditions flipped around:
SELECT CASE WHEN COUNT(CASE WHEN is_senior = true THEN 1 ELSE 0 END) >
COUNT(CASE WHEN location = 'US' THEN 1 ELSE 0 END) THEN 'More seniors'
ELSE 'More USA-based' END AS what_do_we_have_more_of
FROM facebook_employees
Result: 'More USA-based'
Can someone please explain why there is a discrepancy here? What is wrong with the query I've written?
I know this problem can be solved with sub-queries but I wanted to try out a CASE WHEN approach specifically. Is this more efficient?
Edit: the solution I wrote with sub-queries (works with conditions reversed)
WITH us_employees AS (
SELECT id, location
FROM facebook_employees
WHERE location = 'US'
),
senior_employees AS (
SELECT id, is_senior
FROM facebook_employees
WHERE is_senior = true
)
SELECT CASE WHEN COUNT(location) < COUNT(is_senior) THEN 'More seniors' ELSE 'More US-based' END AS what_do_we_have_more_of
FROM us_employees u
FULL JOIN senior_employees s
ON u.id = s.id
Result: 'More seniors'
The use of count() in your query is incorrect. But how do you get different results? Because both counts are the same. So A > B is always false, and you always end up in the ELSE branch.
A proper query could look like this:
SELECT CASE WHEN count(*) FILTER (WHERE location = 'US')
> count(*) FILTER (WHERE is_senior) THEN 'More USA-based'
WHEN count(*) FILTER (WHERE location = 'US')
< count(*) FILTER (WHERE is_senior) THEN 'More seniors'
ELSE 'US-based and seniors tie' END AS what_do_we_have_more_of
FROM facebook_employees;
See:
Aggregate columns with additional (distinct) filters
Note, this can never fail with NULL values, because count() (unlike most aggregate functions) never returns NULL.
You should use SUM instead of COUNT.
COUNT will count +1 even when your CASE return 1 or 0.
SUM only count +1 when your CASE return 1.
So assume that your table has 1000 rows, then both your two queries will be CASE 1000 > 1000 THEN ... ELSE... END.

Division in Sql returning 0 or NULL

New to SQL and trying to figure this one out. I’m expecting a ratio of win % but am getting a 0 or null column
With previous as (select Venue, Row,
Count(case when place = 1 then 1 else null end) as firstx
Count(case when place = 2 then 1 else null end) as secondx
Count(case when place = 3 then 1 else null end) as thirdx
Count(case when place Between 1 and 15 then 1 else null end) as allplaces
From horses
Where racetitle not like ‘%stand%’
Group by venue, row)
Select case
When firstx = 0
Then Null
Else round(firstx/allplaces, 2)
End as winratio
From previous
Where allplaces > 10;
I want to do this with secondx and thirdx but am not sure why it is returning a column as 0 or Null???
You are getting NULL because firstx = 0 and 0 because the rdbms that you use does integer division when dividing firstx/allplaces, so multiply by 1.0 before you divide:
With previous as (
select Venue, Row,
Count(case when place = 1 then 1 end) as firstx
Count(case when place = 2 then 1 end) as secondx
Count(case when place = 3 then 1 end) as thirdx
Count(case when place Between 1 and 15 then 1 end) as allplaces
From horses
Where racetitle not like ‘%stand%’
Group by venue, row
)
Select case
When firstx = 0 Then Null
Else round(1.0 * firstx/allplaces, 2)
End as winratio
From previous
Where allplaces > 10;
I also simplified the CASE expressions inside COUNT() because there is no need for else NULL since this is the default behavior.
I assume that your DBMS makes integer division when / operates on integers. And since firstx is in general less than allplaces, then integer division of them is 0
You need to cast of of the values to a decimal number. The syntax depends on specific DBMS, usually it's CAST or CONVERT function
Some databases do integer division, which is probably the issue you are facing. That said, the more canonical way to prevent a divide-by-zero is to use nullif():
select round(firstx * 1.0/ nullif(allplaces, 0), 2) as winratio
nullif() is a standard SQL function and available in almost all databases.

select query result filter using if conditions

I have folllowing select query
SELECT
Table.ID
SUM(CASE WHEN Table.Status = 1 THEN 1 ELSE null END) AS NormalCount,
SUM(CASE WHEN Table.status = 2 THEN 1 ELSE null END) AS AbnormalCount
FROM Table
GROUP BY Table.ID
I want to get above results and generate new result set with following conditions
IF(NormalCount > 0 or AbnormalCount == NULL)
SELECT
Table.ID
Table.Status AS "Normal"
FROM Table
GROUP BY Table.ID
ELSE IF ( AbnormalCount > 0)
SELECT
Table.ID
Table.Status AS "Abnormal"
SUM(CASE WHEN Header.status = 2 THEN 1 ELSE null END) AS AbnormalCount
FROM Table
GROUP BY Table.ID
I think the logic you want is to label each ID group as being abnormal if it has one or more abnormal observation. If so, then you can use another CASE statement to check the conditional abnormal sum and label the status appropriately. Normal groups would have the characteristic of having an abnormal count of zero, but this count would appear for all groups.
SELECT t.ID,
CASE WHEN SUM(CASE WHEN t.status = 2 THEN 1 ELSE 0 END) > 0
THEN "Abnormal"
ELSE "Normal" END AS Status,
SUM(CASE WHEN t.status = 2 THEN 1 ELSE 0 END) AS AbnormalCount
FROM Table t
GROUP BY t.ID

How to check if all rows validate a predicate

I've a table in my database for which I need to check if all rows have one field not null.
If there are no row or if there is at least 1 row with the field null => true
If there are rows and they are all with the field not null => False
Is there a way to do this in on simple query? Or I need to check if my table is empty first then if it's not check if I've a row with the field value empty ?
This will count how many NULL values you have in a field;
SELECT
SUM(CASE WHEN FieldName IS NULL THEN 1 ELSE 0 END) NullValues
FROM TableName
Will return 0 if there are no NULL values, and will return the number of NULLS if there are any present.
If you actually want to return a value as 'True' or 'False' then do this;
SELECT CASE
WHEN a.NullValues > 0
THEN 'True'
ELSE 'False'
END CheckField
FROM (
SELECT
SUM(CASE WHEN FieldName IS NULL
THEN 1
ELSE 0
END) NullValues
FROM TableName
) a
Use count(*) and count(field) and compare the two:
select
case when count(*) > 0 and count(*) = count(field) then 1 -- not empty and no nulls
else 0 end as isgood
from mytable;
Oracle SQL has no boolean data type , so I use 1 for true and 0 for false. You can replace this with whatever you like (e.g. 'true' instead of 1 and 'false' instead of 0).
As to turning this into a predicate (correlated to a main query), you'd use something along the lines of:
select ...
from main
where exists
(
select 1
from mytable
where mytable.colx = main.coly
having count(*) > 0 and count(*) = count(field)
);
You can do this with aggregation. However, it is difficult to understand what you are asking for. If you want to check that a field has no NULL values, you can do:
select (case when count(*) > 0 then 1 else 0 end) as HasNullValues
from t
where field is null;
Alternate way I found using max with putting null first:
select case when
max(field) keep (dense_rank first order by datfin desc nulls first) is null then 1
else 0 end as flag
from MYTABLE;

Count rows for two columns using two different clauses

I'm after a CTE which I want to return two columns, one with the total number of 1's and one with the total number of 0's. Currently I can get it to return one column with the total number of 1's using:
WITH getOnesAndZerosCTE
AS (
SELECT COUNT([message]) AS TotalNo1s
FROM dbo.post
WHERE dbo.checkletters([message]) = 1
--SELECT COUNT([message]) AS TotalNo0s
--FROM dbo.post
--WHERE dbo.checkletters([message]) = 0
)
SELECT * FROM getOnesAndZerosCTE;
How do I have a second column called TotalNo0s in the same CTE which I have commented in there to show what I mean.
Using conditional aggregation:
WITH getOnesAndZerosCTE AS(
SELECT
TotalNo1s = SUM(CASE WHEN dbo.checkletters([message]) = 1 THEN 1 ELSE 0 END),
TotalNo0s = SUM(CASE WHEN dbo.checkletters([message]) = 0 THEN 1 ELSE 0 END)
FROM post
)
SELECT * FROM getOnesAndZerosCTE;
For using COUNT() directly just be aware that it counts any NON-NULL values. You can omit the ELSE condition which implicitly returns NULL if not stated
SELECT
COUNT(CASE WHEN dbo.checkletters([message]) = 1 THEN 1 END) TotalNo1s
, COUNT(CASE WHEN dbo.checkletters([message]) = 0 THEN 1 END) TotalNo0s
FROM post
or, explicitly state NULL
SELECT
COUNT(CASE WHEN dbo.checkletters([message]) = 1 THEN 1 ELSE NULL END) TotalNo1s
, COUNT(CASE WHEN dbo.checkletters([message]) = 0 THEN 1 ELSE NULL END) TotalNo0s
FROM post
You can do it without CTE
select count(message) total,
dbo.checkletters(message) strLength
from post
group by dbo.checkletters(message)
having dbo.checkletters(message) in (1, 2) //All the messages with length 1 or 2