Division in Sql returning 0 or NULL - sql

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.

Related

SQL : Count with "==" to check in SQL Server [duplicate]

This question already has answers here:
Conditional Count on a field
(8 answers)
Closed last year.
I'm working with SQL Server and I want to create a view (my_View) which has these columns :
[element_1]
[element_2]
[element_3]
[element_4]
All of them are referring to the same column, named [parent_1], in another table (AT)
[parent_1] can have 4 possible values [value_1], [value_2], [value_3] and [value_4].
What I want is that, using COUNT,
[element_1] is equal to the number of times that [parent_1] is equal to [value_1]
same for [element_2], [element_3] and [element_4] equals to [value_2], [value_3] and [value_4].
Is it possible to use "==" inside the COUNT to see if it checks the criteria?
Something like this:
COUNT (AT.parent_1 == "value_1") AS element_1
COUNT (AT.parent_1 == "value_2") AS element_2
COUNT (AT.parent_1 == "value_3") AS element_3
COUNT (AT.parent_1 == "value_4") AS element_4
Thanks guys
You can use the CASE instruction for that
https://learn.microsoft.com/fr-fr/sql/t-sql/language-elements/case-transact-sql?view=sql-server-ver15
SUM (CASE WHEN AT.parent_1 = 'value_4' THEN 1 ELSE 0 END) as element_4
You can do something like:
SELECT
sum(case when parent_1 = "value_1" then 1 else 0 end) as element_1,
sum(case when parent_1 = "value_2" then 1 else 0 end) as element_2,
sum(case when parent_1 = "value_3" then 1 else 0 end) as element_3,
sum(case when parent_1 = "value_4" then 1 else 0 end) as element_4
FROM table;
create my_view as
select
-- other list of columns,
SUM (CASE WHEN AT.parent_1 = 'value_1' THEN 1 ELSE 0 END) as element_1,
SUM (CASE WHEN AT.parent_1 = 'value_2' THEN 1 ELSE 0 END) as element_2,
SUM (CASE WHEN AT.parent_1 = 'value_3' THEN 1 ELSE 0 END) as element_3,
SUM (CASE WHEN AT.parent_1 = 'value_4' THEN 1 ELSE 0 END) as element_4
from tableName AT
There is no need to use == like that of a programming language, in SQL comparison operator is =

SQL or operator how to use with having

i have a table which i am joining with with operator. I can have 2 combinations id that table FDEL - 1 or 0 and FDVE 1 or 0, what i would like to do is to dispay if - item has fdve, or item has fdel (and count) but it doesnt work (i can see all fdve, or all fdel)
select
lpad(purchase_id,10,0) as purchase_id,
sum(has_label_fdel) as FDEL_count,
case when LABELS like '%FDVE%' then 1 else 0 end as HAS_LABEL_FDVE,
sum(has_label_fdve) as FDVE_count
from
"SRC_ORACLEIWP"."PURCHASE_ANALYSIS_RULES"
group by
lpad(purchase_id,10,0),has_label_fdve
having FDVE_count>0 -- FDEL_count>0
You want one resut row per product, so group by product only. Use SUMfor counting and MAX for the aggregated yes/no.
select
lpad(purchase_id,10,0) as padded_purchase_id,
max(has_label_fdve) as has_labels_fdve,
sum(has_label_fdve) as fdve_count,
max(has_label_fdel) as has_labels_fdel,
sum(has_label_fdel) as fdel_count
from src_oracleiwp.purchase_analysis_rules
group by padded_purchase_id
having has_labels_fdve = 1
or has_labels_fdel = 1
order by padded_purchase_id;
I've changed your alias names slightly, so they are digfferent from the columns you have (because such ambiguities can sometimes lead to problems).
The check on labels like '%FDVE%' is unnecessary, because you already have the has_label_fdve flag, which is always 0 or 1. Or so it seems. If the flags can be null, use COALESCE on them or do use LIKE expressions.
If you don't have has_label_fdve and has_label_fdel yet, use the labels column instead:
select
lpad(purchase_id,10,0) as padded_purchase_id,
max(case when labels like '%FDVE%' then 1 else 0 end) as has_labels_fdve,
sum(case when labels like '%FDVE%' then 1 else 0 end) as fdve_count,
max(case when labels like '%FDEL%' then 1 else 0 end) as has_labels_fdel,
sum(case when labels like '%FDEL%' then 1 else 0 end) as fdel_count
from src_oracleiwp.purchase_analysis_rules
group by padded_purchase_id
having has_labels_fdve = 1
or has_labels_fdel = 1
order by padded_purchase_id;

Sum a column and perform more calculations on the result? [duplicate]

This question already has an answer here:
How to use an Alias in a Calculation for Another Field
(1 answer)
Closed 3 years ago.
In my query below I am counting occurrences in a table based on the Status column. I also want to perform calculations based on the counts I am returning. For example, let's say I want to add 100 to the Snoozed value... how do I do this? Below is what I thought would do it:
SELECT
pu.ID Id, pu.Name Name,
COUNT(*) LeadCount,
SUM(CASE WHEN Status = 'Working' THEN 1 ELSE 0 END) AS Working,
SUM(CASE WHEN Status = 'Uninterested' THEN 1 ELSE 0 END) AS Uninterested,
SUM(CASE WHEN Status = 'Converted' THEN 1 ELSE 0 END) AS Converted,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END) AS Snoozed,
Snoozed + 100 AS Test
FROM
Prospects p
INNER JOIN
ProspectsUsers pu on p.OwnerId = pu.SalesForceId
WHERE
p.Store = '108'
GROUP BY
pu.Name, pu.Id
ORDER BY
Name
I get this error:
Invalid column name 'Snoozed'.
How can I take the value of the previous SUM statement, add 100 to it, and return it as another column? What I was aiming for is an additional column labeled Test that has the Snooze count + 100.
You can't use one column to create another column in the same way that you are attempting. You have 2 options:
Do the full calculation (as #forpas has mentioned in the comments above)
Use a temp table or table variable to store the data, this way you can get the first 5 columns, and then you can add the last column or you can select from the temp table and do the last column calculations from there.
You can not use an alias as a column reference in the same query. The correct script is:
SELECT
pu.ID Id, pu.Name Name,
COUNT(*) LeadCount,
SUM(CASE WHEN Status = 'Working' THEN 1 ELSE 0 END) AS Working,
SUM(CASE WHEN Status = 'Uninterested' THEN 1 ELSE 0 END) AS Uninterested,
SUM(CASE WHEN Status = 'Converted' THEN 1 ELSE 0 END) AS Converted,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END)+100 AS Snoozed
FROM
Prospects p
INNER JOIN
ProspectsUsers pu on p.OwnerId = pu.SalesForceId
WHERE
p.Store = '108'
GROUP BY
pu.Name, pu.Id
ORDER BY
Name
MSSQL does not allow you to reference fields (or aliases) in the SELECT statement from within the same SELECT statement.
To work around this:
Use a CTE. Define the columns you want to select from in the CTE, and then select from them outside the CTE.
;WITH OurCte AS (
SELECT
5 + 5 - 3 AS OurInitialValue
)
SELECT
OurInitialValue / 2 AS OurFinalValue
FROM OurCte
Use a temp table. This is very similar in functionality to using a CTE, however, it does have different performance implications.
SELECT
5 + 5 - 3 AS OurInitialValue
INTO #OurTempTable
SELECT
OurInitialValue / 2 AS OurFinalValue
FROM #OurTempTable
Use a subquery. This tends to be more difficult to read than the above. I'm not certain what the advantage is to this - maybe someone in the comments can enlighten me.
SELECT
5 + 5 - 3 AS OurInitialValue
FROM (
SELECT
OurInitialValue / 2 AS OurFinalValue
) OurSubquery
Embed your calculations. opinion warning This is really sloppy, and not a great approach as you end up having to duplicate code, and can easily throw columns out-of-sync if you update the calculation in one location and not the other.
SELECT
5 + 5 - 3 AS OurInitialValue
, (5 + 5 - 3) / 2 AS OurFinalValue
You can't use a column alias in the same select. The column alias do not precedence / sequence; they are all created after the eval of the select result, just before group by and order by.
You must repeat code :
SELECT
pu.ID Id,pu.Name Name,
COUNT(*) LeadCount,
SUM(CASE WHEN Status = 'Working' THEN 1 ELSE 0 END) AS Working,
SUM(CASE WHEN Status = 'Uninterested' THEN 1 ELSE 0 END) AS Uninterested,
SUM(CASE WHEN Status = 'Converted' THEN 1 ELSE 0 END) AS Converted,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END) AS Snoozed,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END)+ 100 AS Test
FROM
Prospects p
INNER JOIN
ProspectsUsers pu on p.OwnerId = pu.SalesForceId
WHERE
p.Store = '108'
GROUP BY
pu.Name, pu.Id
ORDER BY
Name
If you don't want to repeat the code, use a subquery
SELECT
ID, Name, LeadCount, Working, Uninterested,Converted, Snoozed, Snoozed +100 AS test
FROM
(SELECT
pu.ID Id,pu.Name Name,
COUNT(*) LeadCount,
SUM(CASE WHEN Status = 'Working' THEN 1 ELSE 0 END) AS Working,
SUM(CASE WHEN Status = 'Uninterested' THEN 1 ELSE 0 END) AS Uninterested,
SUM(CASE WHEN Status = 'Converted' THEN 1 ELSE 0 END) AS Converted,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END) AS Snoozed
FROM Prospects p
INNER JOIN ProspectsUsers pu on p.OwnerId = pu.SalesForceId
WHERE p.Store = '108'
GROUP BY pu.Name, pu.Id) t
ORDER BY Name
or a view

How to add multiple case in sql

I want to add multiple cases in single line. In other words in below example if first item is NOT NULL and second item is equal to 1 then output should be 1 else 0. How can I do it?
MatchbyCatalog= (case when ISNULL(pc.ProductID,0) and tc.RawMatch=1) then 1 else 0 end)
Is this what you want?
CASE WHEN pc.ProductID IS NULL and tc.RawMatch=1 THEN 1 ELSE 0 END
You misunderstood ISNULL() , it's a function that replaces the value if it's NULL, it's not a condition . You were looking for IS NULL
That's all you need.
MatchbyCatalog= (case when ISNULL(pc.ProductID,0)=1 and tc.RawMatch=1 then 1 else 0 end)
when you say both I think you mean pc.ProductID and tc.RawMatch...
I have not you're entire sql query so I have to guess.
You have multiple solutions :
Nearly ss you have wrote when pc.ProductId = 1 and tc.RawMatch=1 then 1 else 0 :
MatchbyCatalog= (case when ISNULL(pc.ProductID,0)=1 and tc.RawMatch=1) then 1 else 0 end)
You could also use a trick : 1 * 1 should be 1 and 1 * 0 =0 so you could write (if tc.Rawmatch is a boolean sort of with values 0 or 1) :
MatchbyCatalog= ISNULL(pc.ProductID,0) * tc.RawMatch
case when {Condition} then {result}
When {Condition} then {result}
Else {result}

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