How to calc Win Rate in SQL? - sql

I am trying to calculate win rate for the below table in SQL but not getting a correct answer
What I am looking for is WON = 1111/1496*100
So far i've got
SELECT Sum( Status = 'Won') /(Select Count(Status))*100 as Win_rate
FROM table
If i run the above it gives me 0.

Your problem is integer division. Both operands of the division are integers, so SQLite forces an integer result. A typical workaround is force decimal context, like:
SELECT 100.0 * sum(Status = 'Won') / Count(*) as Win_rate FROM mytable
But it is simpler to use avg() here:
SELECT avg(Status = 'Won') * 100 as Win_rate
FROM mytable

Related

Out of range integer: infinity

So I'm trying to work through a problem thats a bit hard to explain and I can't expose any of the data I'm working with but what Im trying to get my head around is the error below when running the query below - I've renamed some of the tables / columns for sensitivity issues but the structure should be the same
"Error from Query Engine - Out of range for integer: Infinity"
WITH accounts AS (
SELECT t.user_id
FROM table_a t
WHERE t.type like '%Something%'
),
CTE AS (
SELECT
st.x_user_id,
ad.name as client_name,
sum(case when st.score_type = 'Agility' then st.score_value else 0 end) as score,
st.obs_date,
ROW_NUMBER() OVER (PARTITION BY st.x_user_id,ad.name ORDER BY st.obs_date) AS rn
FROM client_scores st
LEFT JOIN account_details ad on ad.client_id = st.x_user_id
INNER JOIN accounts on st.x_user_id = accounts.user_id
--WHERE st.x_user_id IN (101011115,101012219)
WHERE st.obs_date >= '2020-05-18'
group by 1,2,4
)
SELECT
c1.x_user_id,
c1.client_name,
c1.score,
c1.obs_date,
CAST(COALESCE (((c1.score - c2.score) * 1.0 / c2.score) * 100, 0) AS INT) AS score_diff
FROM CTE c1
LEFT JOIN CTE c2 on c1.x_user_id = c2.x_user_id and c1.client_name = c2.client_name and c1.rn = c2.rn +2
I know the query works for sure because when I get rid of the first CTE and hard code 2 id's into a where clause i commented out it returns the data I want. But I also need it to run based on the 1st CTE which has ~5k unique id's
Here is a sample output if i try with 2 id's:
Based on the above number of row returned per id I would expect it should return 5000 * 3 rows = 150000.
What could be causing the out of range for integer error?
This line is likely your problem:
CAST(COALESCE (((c1.score - c2.score) * 1.0 / c2.score) * 100, 0) AS INT) AS score_diff
When the value of c2.score is 0, 1.0/c2.score will be infinity and will not fit into an integer type that you’re trying to cast it into.
The reason it’s working for the two users in your example is that they don’t have a 0 value for c2.score.
You might be able to fix this by changing to:
CAST(COALESCE (((c1.score - c2.score) * 1.0 / NULLIF(c2.score, 0)) * 100, 0) AS INT) AS score_diff

Get column sum and use to calculate percent of total, why doesn't work with CTEs

I did this following query, however it gave the the result of 0 for each orderStatusName, does anyone know where is the problem?
with tbl as (
select s.orderstatusName, c.orderStatusId,count(c.orderId) counts
from [dbo].[ci_orders] c left join
[dbo].[ci_orderStatus] s
on s.orderStatusId = c.orderStatusId
where orderedDate between '2018-10-01' and '2018-10-29'
group by orderStatusName, c.orderStatusId
)
select orderstatusName, counts/(select sum(counts) from tbl as PofTotal) from tbl
the result is :0
You're using what is known as integer math. When using 2 integers in SQL (Server) the return value is an integer as well. For example, 2 + 2 = 4, 5 * 5 = 25. The same applies to division 8 / 10 = 0. That's because 0.8 isn't an integer, but the return value will be one (so the decimal points are lost).
The common way to change this behaviour is to multiply one of the expressions by 1.0. For example:
counts/(select sum(counts) * 1.0 from tbl) as PofTotal
If you need more precision, you can increase the precision of the decimal value of 1.0 (i.e. to 1.000, 1.0000000, etc).
Use window functions and proper division:
select orderstatusName, counts * 1.0 / total_counts
from (select t.*, sum(counts) over () as total_counts
from tbl
) t;
The reason you are getting 0 is because SQL Server does integer division when the operands are integers. So, 1/2 = 0, not 0.5.

SQL Server Percent Difference is Greater than Value

I have the following table structures:
table c_alert:
|dynamic|symbol|price_usd|
--------------------------
|5 |BTC |13000 |
table c_current:
|symbol|price_usd|
------------------
|BTC |13600 |
I have this query:
SELECT dbo.c_alert.symbol, dbo.c_alert.price_usd AS alert_price, dbo.c_current.price_usd AS current_price, (dbo.c_current.price_usd - dbo.c_alert.price_usd) * 100.0 / dbo.c_alert.price_usd AS pct_diff, dbo.c_alert.dynamic AS pct
FROM dbo.c_alert INNER JOIN
dbo.c_current
ON dbo.c_alert.symbol = dbo.c_current.symbol AND
dbo.c_alert.dynamic > (dbo.c_current.price_usd - dbo.c_alert.price_usd) * 100.0 / dbo.c_alert.price_usd
Which returns this:
|symbol|alert_price|current_price|pct_diff|dynamic|
-----------------------------------------------
|BTC |13000 |13613.3000 |4.7 |5 |
Not very strong with financial queries...Basically I would like to know when the price difference between alert_price and current_price are equal to or greater than value in the dynamic column as a boolean. So where the difference is equal or greater than 5% show True, else False. That dynamic value (integer) could change for each row in the c_alert table. Hope someone can provide a solution to the query.
Because the same percent difference term is required in multiple places in the query, I might go with using a CTE first, which calculates this term. Then, do a straightforward query on the CTE to get the output you want.
WITH cte AS (
SELECT
t2.symbol,
t2.dynamic,
t2.price_usd AS alert_price,
t1.price_usd AS current_price,
100.0*(t1.price_usd - COALESCE(t2.price_usd, 0.0)) / t2.price_usd AS pct_diff
FROM dbo.c_current t1
LEFT JOIN dbo.c_alert t2
ON t1.symbol = t2.symbol
)
SELECT
symbol,
alert_price,
current_price,
pct_diff,
dynamic,
CASE WHEN pct_diff > dynamic THEN 'TRUE' ELSE 'FALSE' END AS result
FROM cte;
Edit:
The logic seems to be working in the demo below. If you still have issues, then edit the demo and paste the link somewhere as a comment.
Demo
Use table aliases so your query is easier to write and to read. Then just use a case:
SELECT a.symbol, a.price_usd AS alert_price,
c.price_usd AS current_price,
(c.price_usd - a.price_usd) * 100.0 / a.price_usd AS pct_diff,
a.dynamic AS pct,
(case when (a.price_usd - c.price_used) > a.dynamic
then 'true' else 'false'
end) as flag
FROM dbo.c_alert a INNER JOIN
dbo.c_current c
ON a.symbol = c.symbol AND
a.dynamic > (c.price_usd - a.price_usd) * 100.0 / a.price_usd;
SQL Server doesn't have a boolean type, so this uses a string. You can use 0 and 1 instead.

Query division returns 0

I am working on a query that devide 2 columns, I tried CAST and CONVERT but still returns 0. Will apperciate your help
SELECT a.Disposition,a.[Disposition Reason Breakdown],a.CSP,b.Total FROM
(
SELECT a.[Disposition],a.[Disposition Reason Breakdown],a.[CSP] FROM
(
SELECT [Disposition],[Disposition Reason Breakdown],COUNT(*) as CSP FROM [dbo].[Disposition]
WHERE [Disposition] <> 'Interested'
GROUP BY [Disposition],[Disposition Reason Breakdown]
) a
)a
INNER JOIN
(
SELECT a.Disposition,SUM(a.CSP) as Total FROM
(
SELECT [Disposition],[Disposition Reason Breakdown],COUNT(*) as CSP FROM [dbo].[Disposition]
WHERE [Disposition] <> 'Interested'
GROUP BY [Disposition],[Disposition Reason Breakdown]
)a
GROUP BY a.Disposition
)b ON a.Disposition = b.Disposition
I am using sql
I solved it, it turns out that I just used the wrong data type which in my case is decimal I should've thought of REAL here is the final query a.CSP/CAST(b.Total as REAL)

T-SQL average rounded to the closest integer

I'm not sure if this has been asked before, but how do I get the average rounded to the closest integer in T-SQL?
This should do it. You might need a GROUP BY on the End depending on what you are looking for the average of.
SELECT CONVERT(int,ROUND(AVG(ColumnName),0))
FROM
TableName
EDIT: This question is more interesting than I first thought.
If we set up a dummy table like so...
WITH CTE
AS
(
SELECT 3 AS Rating
UNION SELECT 4
UNION SELECT 7
)
SELECT AVG(Rating)
FROM
CTE
We get an integer average of 4
However if we do this
WITH CTE
AS
(
SELECT 3.0 AS Rating
UNION SELECT 4.0
UNION SELECT 7.0
)
SELECT AVG(Rating)
FROM
CTE
We get a decimal average of 4.666..etc
So it looks like the way to go is
WITH CTE
AS
(
SELECT 3 AS Rating
UNION SELECT 4
UNION SELECT 7
)
SELECT CONVERT(int,ROUND(AVG(CONVERT(decimal,Rating)),0))
FROM CTE
Which will return an integer value of 5 which is what you are looking for.
If you are in SQL Server, just use round(avg(column * 1.0), 0).
The reason for * 1.0 is because sql server in some cases returns calculations using the same datatype of the values used in the calculation. So, if you calculate the average of 3, 4 and 4, the result is 3.66..., but the datatype of the result is integer, therefore the sql server will truncate 3.66... to 3, using * 1.0 implicit convert the input to a decimal.
Alternatively, you can convert or cast the values before the average calculation, like cast(column as decimal) instead of using the * 1.0 trick.
If your column it's not a integer column, you can remove the * 1.0.
PS: the result of round(avg(column * 1.0), 0) still is a decimal, you can explicit convert it using convert(int, round(avg(column * 1.0), 0), 0) or just let whatever language you are using do the job (it's a implicit conversion)
Select cast(AVG(columnname) as integer)
This worked for it:
CONVERT(int,ROUND(AVG(CAST(COLUMN-NAME AS DECIMAL)) ,0))
Isn't there a shorter way of doing it though?
T-SQL2018.
CAST(ROUND(COLUMN, 0) AS INT) This code does the job for me and gives the output I require so a 4.8 becomes 5.
whereas
CAST(AVG(COLUMN) AS INT) This code almost does the job but rounds down, so 4.8 becomes a 4 and not 5.
select cast(avg(a+.5) as int) from
(select 1 a union all select 2) b
If you don't like shortcuts, you could use the long way:
select round(avg(cast(a as real)), 0)
from (select 1 a union all select 2) b
The following statements are equivalent:
-- the original code
CONVERT(int, ROUND(AVG(CAST(mycolumn AS DECIMAL)) ,0))
-- using '1e0 * column' implicitly converts mycolumn value to float
CONVERT(int, ROUND(AVG(1e0 * mycolumn) ,0))
-- the conversion to INT already rounds the value
CONVERT(INT, AVG(1e0 * mycolumn))
On SQL 2014,
select round(94,-1)
select round(95,-1)