In my select list, I want to return a column that shows the percent of appointments missed. When a client misses an appointment, the source column contains "0" for duration. So I would like to sum the instances of "0" and divide by total appointments scheduled.
CAST(SUM(CASE WHEN event_client_duration = 0 THEN 1 ELSE 0 END)/COUNT(event_key) AS FLOAT)
That does not throw an error, but it returns a value of 0 except in the rare case when appointments missed are more than 50% in which case it returns 1.
SUM(CASE WHEN event_client_duration = 0 THEN 1 ELSE 0 END) -- that works
COUNT(event_key) -- that also works, but together they bonk
So, I tried to cast it as a decimal, but this causes an arithmetic overflow.
CAST(SUM(CASE WHEN e_cl_dur = 0 THEN 1 ELSE 0 END)/COUNT(e_key) AS DECIMAL(2,2))
I also tried using percent as a data type. Any ideas?
SUM(CASE event_client_duration WHEN 0 THEN 1.0 ELSE 0.0 END) / COUNT(event_key)
Related
I have a table which comprises of 30 columns, all adjacent to one another. Of these 5 are text fields indicating certain details pertaining to that entry and 25 are value fields. Value fields have the column name as Val00, Val01, Val02 .....upto Val24
Based on a logic appearing elsewhere, these value fields input a value for n amount of columns and then drop to 0 for all of the subsequent fields
e.g.
When n is 5 the output will be
Val00
Val01
Val02
Val03
Val04
Val05
Val06
Val07
Val24
1.5
1.5
1.5
1.5
1.5
0
0
0
0
As can be seen, all values starting from val05 will drop to 0 and all columns from Val 05 to Val24 will be 0.
Given this output, I want to find what this n value is and create a new column ValCount to store this value in it.
In Excel this would be fairly straight forward to achieve with the below formula
=COUNTIF(Val00:Val24,">0")
However I'm not sure how we would go about in terms of SQL. I understand that the count function works on a columnar level and not on a row level.
I found a solution which is rather long but it should do the job so I hope it helps you.
SELECT SUM(SUM(CASE WHEN Val00 >= 1 THEN 1 ELSE 0 END)
+ SUM(CASE WHEN Val01 >= 1 THEN 1 ELSE 0 END)
+ SUM(CASE WHEN Val02 >= 1 THEN 1 ELSE 0 END)) As ValCount
I have 3 nvarchar columns user_3, user_4 and description. I am setting yes and no flag. If the value in description column is equal or in between user_3 and User4 then set the flag to 'N' else
set the flag to 'Y'.
Here is the SQL script I wrote so far. it works in some instances but not always. See the image with results. it worked on line #1 but didn't work on line # 6 for example. What am I doing wrong?
SELECT [B].USER_3,[B].USER_4,A.DESCRIPTION,
(case when Isnumeric(A.DESCRIPTION) <> 1 then 'Y'
else case when (CASE WHEN Isnumeric(A.DESCRIPTION) = 1 then
cast(A.DESCRIPTION AS decimal(10,5)) else 0 end)
between ( CASE WHEN Isnumeric([B].USER_4) = 1 then
cast([B].USER_4 AS decimal(10,5)) else 0 end) and
(CASE WHEN Isnumeric([B].USER_3) = 1 then cast([B].USER_3 AS decimal(10,5)) else 0 end)
then 'N' else 'Y' end end) as Flagset
from A , B
Here is the screenshot of the results
enter image description here
The issue is with your use of BETWEEN as per the docs:
BETWEEN returns TRUE if the value of test_expression is greater than or equal to the value of begin_expression and less than or equal to the value of end_expression.
Because you don't know whether USER_3 or USER_4 is the higher limit or the lower limit, you need to test both ways.
Note: For this sort of query I prefer to pre-calculate all the values (using CROSS APPLY in this case) I need. It makes it much easier to follow and debug.
SELECT USER_3, USER_4, [DESCRIPTION]
, CASE WHEN ISNUMERIC([DESCRIPTION]) <> 1 THEN 'Y' ELSE
CASE WHEN CASE WHEN ISNUMERIC([DESCRIPTION]) = 1 THEN CAST([DESCRIPTION] AS decimal(10,5)) ELSE 0 END BETWEEN CASE WHEN ISNUMERIC(USER_4) = 1 THEN CAST(USER_4 AS decimal(10,5)) ELSE 0 END AND
CASE WHEN ISNUMERIC(USER_3) = 1 THEN CAST(USER_3 AS decimal(10,5)) ELSE 0 END
THEN 'N' ELSE 'Y' END END AS Flagset
, CASE WHEN DNUMERIC <> 1 THEN 'Y' ELSE CASE WHEN DESCRIPTIOND BETWEEN USER_4D AND USER_3D OR DESCRIPTIOND BETWEEN USER_3D AND USER_4D THEN 'N' ELSE 'Y' END END CorrectedFlagSet
FROM (VALUES
('1.395','1.385','1.390')
, ('22.025','41.425','22')
, ('22.025','41.425','23.025')
) AS X (USER_3, USER_4, [DESCRIPTION])
CROSS APPLY (VALUES (
CASE WHEN ISNUMERIC(USER_3) = 1 THEN CAST(USER_3 AS decimal(10,5)) ELSE 0 END
, CASE WHEN ISNUMERIC(USER_4) = 1 THEN CAST(USER_4 AS decimal(10,5)) ELSE 0 END
, CASE WHEN ISNUMERIC([DESCRIPTION]) = 1 THEN CAST([DESCRIPTION] AS decimal(10,5)) ELSE 0 END
, CASE WHEN ISNUMERIC([DESCRIPTION]) = 1 THEN 1 ELSE 0 END
)) AS Y (USER_3D, USER_4D, DESCRIPTIOND, DNUMERIC);
Returns:
USER_3
USER_4
DESCRIPTION
Flagset
CorrectedFlagSet
1.395
1.385
1.390
N
N
22.025
41.425
22
Y
Y
22.025
41.425
23.025
Y
N
I'm sure I don't have to mention that you should really be storing this data in numeric form in the first place as it will perform better and save you lots of future issues.
And well laid out queries with consistent casing also helping understand and debug them.
Finally providing a minimal reproducible example with sample data, your query and your desired result as shown here makes it much easier for people to assist.
In SQL Server for calculating percentage I have a function like below:
CREATE FUNCTION [dbo].[fuGetPercentage] ( #part FLOAT, #total FLOAT )
RETURNS FLOAT
AS
BEGIN
DECLARE #Result FLOAT = 0, #Cent FLOAT = 100;
IF (isnull(#total, 0) != 0)
SET #Result = isnull(#part, 0) * #Cent / #total;
RETURN #Result
END
I wonder that is there any better alternative for that, with same checks and a better calculating percentage like below:
SELECT (CASE ISNULL(total, 0)
WHEN 0 THEN 0
ELSE ISNULL(part, 0) * 100 / total
END) as percentage
I want to use it directly after SELECT like above.
There is one issue with using functions such as ISNULL. The query will not use indexes in that case. If the beauty of the code isn't in the first place then you can do something like that:
SELECT
CASE WHEN total * part <> 0 /* will check that both total and part are not null and != 0*/
THEN part * 100 / total
ELSE 0
END AS percentage;
I use #DmitrijKultasev answer but now, I found that it has two problems:
Error on conversion because of the overflow of result of multiply.
Performance problem; because of that math multiply and its conversion.
So I change it to this:
SELECT
CASE
WHEN total <> 0 AND part <> 0 THEN -- This will return 0 for Null values
part * 100 / total
ELSE
0
END AS percentage;
I'm in the process of creating some reports that take a finite total (lets say 2,500) of products (for this example lets say Ice Cream Cones) and counts how many of them were broken before serving.
Now the actual count code of broken cones I've got down.
SELECT COUNT(broken_cones) FROM [ice].[ice_cream_inventory]
WHERE broken_cones = 'Yes'
However, I need a percentage of broken cones from this total as well. I've been playing around with the code but I keep running into a 'Divide By Zero' error with this code below.
SELECT CAST(NULLIF((.01 * 2500)/Count(broken_cones), 0) AS
decimal(7,4)) FROM [ice].[ice_cream_inventory] WHERE broken_cones = 'Yes'
For right now, there aren't any broken cones (and won't be for a while) so the total right now is zero. How can I show the NULL scenario as zero?
I tried to place an ISNULL statement in the mix but I kept getting the 'Divide by Zero' error. Am I even doing this right?
::edit::
Here's what I ended up with.
SELECT
CASE
WHEN COUNT(broken_cones) = 0 then 0
ELSE CAST(NULLIF((.01 * 2500)/Count(broken_cones), 0) AS decimal(7,4))
END
FROM [ice].[ice_cream_inventory] WHERE broken_cones = 'Yes'
Use a case statement.
SELECT
CASE WHEN COUNT(broken_cones) = 0 then 0
ELSE CAST(NULLIF((.01 * 2500)/Count(broken_cones), 0) AS decimal(7,4)) END
FROM [ice].[ice_cream_inventory] WHERE broken_cones = 'Yes'
You already have a solution, but this is why your original solution didn't work.
Your NULLIF needs to be moved in order to be effective. It is doing the division before it gets to the NULLIF call. Dividing by null will return a null value.
SELECT CAST((.01 * 2500)/NULLIF(Count(broken_cones), 0) AS decimal(7,4))
FROM [ice].[ice_cream_inventory]
WHERE broken_cones = 'Yes'`
The NULLIF() function is a great way to prevent divide by zero, since anything divide by NULL returns null. The way to use it is as follows:
<expression> / NULLIF( <expression>, 0 )
Unfortunately you've wrapped your whole divide expression in NULLIF() which is why it isn't working for you. So step one is to get it to return NULL if your COUNT() comes back zero:
SELECT
(0.01 * 2500) / NULLIF( COUNT(broken_cones), 0 )
FROM [ice].[ice_cream_inventory]
WHERE broken_cones = 'Yes'
Now you said you wanted that NULL to come back zero? That is where you use ISNULL():
ISNULL(<expression1>, <expression2>)
If the first expression is NULL then return the second expression, so our SQL now becomes:
SELECT
ISNULL(
(0.01 * 2500) / NULLIF( COUNT(broken_cones), 0 ),
0
)
FROM [ice].[ice_cream_inventory]
WHERE broken_cones = 'Yes'
I have a field which is varchar and contains numbers and dates as strings. I want to update all numbers in this field that is greater than 720. I have attempted firstly to do a select but I get this error:
Conversion failed when converting the nvarchar value '16:00' to data type int.
This is my query:
select id, case(isnumeric([other08])) when 1 then [other08] else 0 end
from CER where sourcecode like 'ANE%' --and other08 > 720
It fails when I uncomment the last part.
I am trying to get all numerics greater than 720, but I can't do the comaprison. It also fails when casting and converting.
Thanks all for any help
You also need to perform the checks and conversion in the WHERE clause:
SELECT
id,
CASE WHEN isnumeric([other08]) = 1 THEN CAST([other08] AS INT) ELSE 0 END
FROM CER
WHERE sourcecode LIKE 'ANE%'
AND CASE WHEN isnumeric([other08]) = 1 THEN CAST([other08] AS INT) ELSE 0 END > 720
You need to use IsNumeric in your where clause, to avoid trying to compare strings to the number 720. Eg:
select id, case(isnumeric([other08])) when 1 then [other08] else 0 end
from CER
where sourcecode like 'ANE%' and ISNUMERIC(other08) = 1 and other08 > 720
EDIT
As #Abs pointed out, the above approach won't work. We can use a CTE to compute a reliable field to filter on, however:
WITH Data AS (
select id
, case WHEN isnumeric([other08]) THEN CAST([other08] AS int) else 0 end AS FilteredOther08
, CER.*
from CER
where sourcecode like 'ANE%'
)
SELECT *
FROM Data
WHERE [FilteredOther08] > 720