Long arithmetic with SQL SELECT statement - sql

The basic idea is I have 3 separate SELECT statements that output a number. I need a percentage from those numbers. So the problem would look like this:
(a.count_one + b.count_two) -
(b.count_two + c.count_three) / (a.count_one + b.Count_two) * 100
When I do the code found below I only get the output of the first (a.count_one + b.count_two). BUT if I comment out the third portion which is / (a.count_one + b.count_two) I successfully get the correct solution from (a.count_one + b.count_two) – (b.count_two + c.count_three).
So it appears, doing math of two statements is great, but when I throw in a third statement it pukes and only wants to show the solution of the first problem (a.count_one + b.count_two), but does not even try to calculate the solution for (a.count_one + b.count_two) – (b.count_two + c.count_three) anymore. I'm a bit stumped why.
Select
(a.Count_one + b.Count_two) -
(b.Count_two + c.Count_three) / (a.Count_one + b.Count_two) * 100
as 'Final_Percentage'
from
(
select COUNT(v_Summary.ResourceID) AS Count_one
From [DB1].[dbo].[v_Summary]
) a,
(
select count([CN]) as Count_two
From [DB2].[dbo].[Computers]
WHERE cn NOT IN (SELECT name0 FROM [DB1].[dbo].[v_system] where v_system.Client0 = '1')
) b,
(
select COUNT(v_Summary.ResourceID) AS Count_three
From [DB1].[dbo].[v_Summary]
Where Description like '%/Fail'
) c
And to give additional information. The math problem with numbers:
(54558 + 373) – (373 + 117) / (54558 + 373) * 100
Or further solved:
(54931) - (490) / 55304 * 100 = 98.44%

COUNT function returns int. When you divide two int values the result is again int in SQL Server. So, 490 / 55304 is 0. Then 0 * 100 is 0 again.
Cast the values to a suitable type, like float.
By the way,
(54931) - (490) / 55304 * 100
is not equal to ~98.44, it is equal to ~54930.11399
You need parentheses to get the result you expect:
((54931) - (490)) / 55304 * 100
is equal to ~98.43953421
So, the final query should look like this:
Select
((a.Count_one + b.Count_two) - (b.Count_two + c.Count_three))
/ (a.Count_one + b.Count_two) * 100
as 'Final_Percentage'
from
(
select CAST(COUNT(v_Summary.ResourceID) AS float) AS Count_one
From [DB1].[dbo].[v_Summary]
) a,
(
select CAST(count([CN]) AS float) as Count_two
From [DB2].[dbo].[Computers]
WHERE cn NOT IN (SELECT name0 FROM [DB1].[dbo].[v_system] where v_system.Client0 = '1')
) b,
(
select CAST(COUNT(v_Summary.ResourceID) AS float) AS Count_three
From [DB1].[dbo].[v_Summary]
Where Description like '%/Fail'
) c

You should also get the right result by saying
Select
100.*(a.Count_one - c.Count_three) / (a.Count_one + b.Count_two)
as 'Final_Percentage'
from ... -- unchanged subqueries, as supplied in your question
By moving the *100. right to the front you implicitly convert the first product to float and the type then stays that way for the rest of the calculation. Also, you might have noticed, I simplified the formula, since b.Count_two gets subtracted from itself, so you can leave it out altogether. I have not tested this but I am pretty sure it will work.

Related

Issue in Selection with CHARINDEX and SUBSTRING

I have 2 questions here.
I have a column 'Campaign' with these entries. My objective is to extract the Cost part from the string and create a new column Cost in my table
29693214 - Live -JUTL Phase 2 Creator Stories Trailer * 7.12 - 7.25 * Video Views * $28,169.01 * BG - Mob
89695072 - Live -WUTL Retargeting JG * 7.16 - 7.31 * Link Clicks * $23,474.18 * KG - Mob
I tried select SUBSTRING(Campaign,CHARINDEX('$',Campaign) +1,???) .I am unable to figure out the '???' part. I want to start with the index next to '$' and continue till the '*' symbol to capture the cost.
While creating a new column I follow these steps
Alter table T ADD NewColumn varchar(100)
then I do this
Update T SET NewColumn = 'Say I want that cost part from the above question here'
Is there any efficient way to do this in single shot?
I think apply makes these operations a bit simpler:
select left(v1.str1, charindex(' ', v1.str1))
from (values ('29693214 - Live -JUTL Phase 2 Creator Stories Trailer * 7.12 - 7.25 * Video Views * $28,169.01 * BG - Mob')) v(str) cross apply
(values (stuff(str, 1, charindex('$', str), ''))) v1(str1);
This can readily be incorporated into an update:
Update t
set newcolumn = left(v.str1, charindex(' ', v.str1))
from t cross apply
(values (stuff(t.str, 1, charindex('$', str), ''))) v(str1)
DDL for testing query:
declare #tbl table(Campaign varchar(200));
insert into #tbl values
('29693214 - Live -JUTL Phase 2 Creator Stories Trailer * 7.12 - 7.25 * Video Views * $28,169.01 * BG - Mob'),
('189695072 - Live -WUTL Retargeting JG * 7.16 - 7.31 * Link Clicks * $23,474.18 * KG - Mob');
If the cost is always separated by a asterisk * from other part of the string, you could use below query:
select trim(substring(Campaign, dollarIndex + 1, asteriskIndex - dollarIndex - 1)) from (
select Campaign, charindex('$', Campaign) dollarIndex,
charindex('*', Campaign, charindex('$', Campaign)) asteriskIndex
from #tbl
) a
To use it in update statement, you could use the same query, but we need to transform above into single query:
update #tbl set Campaign = trim(substring(Campaign, charindex('$', Campaign) + 1, charindex('*', Campaign, charindex('$', Campaign)) - charindex('$', Campaign) - 1))
Note: consider if it's worthy keeping data that you already have (redundancy). If it can be always be parsed from your column in the same way you could use view for this task.
On the other hand it might be qiute expensive to query such data and might be bad for performance.

Divide by zero error in SQL even though I excluded the zero cases?

Here is the code that I've been trying to run:
SELECT C.* FROM
(SELECT
B.[OUTSIDE_ROW],
B.[INSIDE_ROW],
B.[r_HU_vac_ns],
B.[r_HU_vac_ns_MOE],
CASE WHEN B.[r_HU_vac_ns] = 0 THEN 999 ELSE B.[r_HU_vac_ns_MOE]/B.[r_HU_vac_ns] END AS [PCT]
FROM
(SELECT
A.[OUTSIDE_ROW],
A.[INSIDE_ROW],
(A.[HU_VACANT] - A.[HU_VACANT_SEASONAL_RECREATIONAL])/A.[HU_VACANT] AS [r_HU_vac_ns],
(1/A.[HU_VACANT]) * POWER(
CASE WHEN ((A.[HU_VACANT] - A.[HU_VACANT_SEASONAL_RECREATIONAL])/A.[HU_VACANT]) * POWER(A.[HU_VACANT_MOE], 2) < POWER(A.[HU_VACANT_MOE], 2) + POWER(A.[HU_VACANT_SEASONAL_RECREATIONAL_MOE], 2) THEN POWER(A.[HU_VACANT_MOE], 2) + POWER(A.[HU_VACANT_SEASONAL_RECREATIONAL_MOE], 2) - (((A.[HU_VACANT] - A. [HU_vACANT_SEASONAL_RECREATIONAL])/A.[HU_VACANT]) * POWER(A.[HU_VACANT_MOE], 2))
ELSE POWER(A.[HU_VACANT_MOE], 2) + POWER(A. [HU_VACANT_SEASONAL_RECREATIONAL_MOE], 2) + (((A.[HU_VACANT] - A. [HU_vACANT_SEASONAL_RECREATIONAL])/A.[HU_VACANT]) * POWER(A.[HU_VACANT_MOE], 2)) END, 0.5) AS [r_HU_vac_ns_MOE]
FROM
(SELECT
[OUTSIDE_ROW],
[INSIDE_ROW],
SUM([ESTIMATE_1]) AS [HU_VACANT],
POWER(SUM(POWER([MOE_1], 2)), 0.5) AS [HU_VACANT_MOE],
SUM([ESTIMATE_2]) AS [HU_VACANT_SEASONAL_RECREATIONAL],
POWER(SUM(POWER([MOE_2], 2)), 0.5) AS [HU_VACANT_SEASONAL_RECREATIONAL_MOE]
FROM #TEST_TABLE
GROUP BY [OUTSIDE_ROW], [INSIDE_ROW]) A
WHERE A.[HU_VACANT] > 0) B ) C
WHERE C.[PCT] < 0.2
Every time I run it, I get the following error:
Msg 8134, Level 16, State 1, Line 533
Divide by zero error encountered.
However, if I take off the last line of code (the following WHERE clause) the code runs fine:
WHERE C.[PCT] < 0.2
Just from looking at my query, can anyone tell me what I'm doing wrong? I thought I eliminated all PCT values that were zero with the CASE WHEN statement below so this error is baffling me:
CASE WHEN B.[r_HU_vac_ns] = 0 THEN 999 ELSE B.[r_HU_vac_ns_MOE]/B.[r_HU_vac_ns] END AS [PCT]
If it helps, PCT is cast as floating point.
Thanks.
SQL Server reserves the right to rearrange calculations. That means that the calculation in a SELECT can happen before filtering occurs. This is true even when the filters are in subqueries and CTEs.
The only way to guarantee order of calculation is CASE. However, I think it is easier to just use NULLIF(), an ANSI standard function. Instead of logic like this:
(A.[HU_VACANT] - A.[HU_VACANT_SEASONAL_RECREATIONAL])/A.[HU_VACANT] AS [r_HU_vac_ns],
do:
(A.[HU_VACANT] - A.[HU_VACANT_SEASONAL_RECREATIONAL])/NULLIF(A.[HU_VACANT], 0) AS [r_HU_vac_ns],
Instead of filtering out the records using
WHERE A.[HU_VACANT] > 0
You should filter out the records at root level
having SUM([ESTIMATE_1]) > 0
You could also use nullif function but that will result producing NULL where you have zero

default value for column in SQL view

I have an SQL view vith many columns.
ID (land parcel ID) ex : 7465-85-7468
Building (ID of a specific building if needed) ex : 102
Room (ID of a specific room if needed) ex : 0023
I have an ID for each row, but for most of the rows, there is not Building ID or Room ID
I'd like to fill the blank rows with '000' for building, and '0000' for Room.
I could Concat these fields in a fourth field (full ID)
It tried to get a default value with "ISNULL", but it didn't work.
For Now, I have somethins like this
ID / Building / Room / Full ID
7848-05-6956 / / / 7848-05-6956--
6985-26-7485 / 102 / 0000 / 6985-26-7485-102-0000
7236-12-0145 / / 0223 / 7236-12-0145--0223
I'd need this
ID / Building / Room / Full ID
7848-05-6956 / 000 / 0000 / 7848-05-6956-000-0000
6985-26-7485 / 102 / 0000 / 6985-26-7485-102-0000
7236-12-0145 / 000 / 0223 / 7236-12-0145-000-0223
If the field is not nullable, the data may be stored as empty strings rather than NULL's.
Try doing something like the following:
SELECT x.ID, x.Building, x.Room, ID + '-' + Building + '-' + Room AS [Full ID]
FROM (
SELECT
ID
, CASE WHEN ISNULL(Building, '') = '' THEN '000' ELSE Building END AS Building
, CASE WHEN ISNULL(Room, '') = '' THEN '0000' ELSE Room END AS Room
FROM MyTable ) x
This will handle both NULL and empty string and it will allow you to build Full ID without executing the CASE statements twice.

Extra parenthesis changing result of formula in SQL Server 2008

In all other languages (arithmetic engines in general) putting an extra set of parenthesis around operators of same priority does not impact results. But recently in a testing project I noticed that MS SQL server changes the results in those cases. Please take a look at the query below, and let me know if you have any idea (or a setting in SQL Server administration) or any links to MSDN article explaining the behavior.
select (0.55 * 287.61 / 0.66) calc_no_parens
,(0.55 * (287.61 / 0.66)) calc_parens
,round(0.55 * 287.61 / 0.66,2) no_paren_round
,round(0.55 * (287.61 / 0.66),2) paren_round;
Results
Column Record 1
calc_no_parens 239.6750000
calc_parens 239.67499985
no_paren_round 239.6800000
paren_round 239.67000000
To me, first two of them should return 239.675, and round should give 239.68.
You will get the desired result if you declare each value as Float.
DECLARE #Float1 float, #Float2 float, #Float3 float;
SET #Float1 = 0.55;
SET #Float2 = 287.61;
SET #Float3 = 0.66;
select (#Float1 * #Float2 / #Float3) calc_no_parens
,(#Float1* (#Float2/ #Float3)) calc_parens
,round(#Float1 * #Float2/ #Float3,2) no_paren_round
,round(#Float1* (#Float2/ #Float3),2) paren_round;
Output
calc_no_parens calc_parens no_paren_round paren_round
239.675 239.675 239.68 239.68
You may want to see this article: So-called "exact" numerics are not at all exact!
I can see what is happening, but I don't think there is a fix.
SQL calculates and stores each part of the function as a SQL data type (in this case it's a floating point number).
287.61/0.66 produces 435.7727272727272727272727272... which SQL will store as a floating point number to some degree of accuracy, however it isn't exact (after all, it's a floating point number).
For more info on floating point numbers: How is floating point stored? When does it matter?
Habib's answer made me thinking this has to be with decimal data types my columns are using. After a bit of research, I found this
Precision, Scale, and Length (Transact-SQL)
As you can see in that article, division operation significantly changes the both scale and precision of resulting decimal. Then I tried an variation of my query, this time adding extra parenthesis around Multiplication operation.
select distinct (0.55 * 287.61 / 0.66) calc_no_parens
,(0.55 * (287.61 / 0.66)) calc_parens_div
,((0.55 * 287.61) / 0.66) calc_parens_mult
,round(0.55 * 287.61 / 0.66,2) no_paren_round
,round(0.55 * (287.61 / 0.66),2) paren_round
,round((0.55 * 287.61) / 0.66,2) paren_round2;
Results
Column Record 1
calc_no_parens 239.6750000
calc_parens_div 239.67499985
calc_parens_mult 239.6750000
no_paren_round 239.6800000
paren_round 239.67000000
paren_round2 239.6800000
So as long as division is the last operator in the formula we get correct answers. Its not a fix to the problem, but a learning to self in any future testing projects.
When you use numbers SQL try to convert them dynamically:
{
SELECT
0.55*(287.61 / 0.66) PrecisionError,
0.55* (CONVERT(NUMERIC(24,12), 287.61) / CONVERT(NUMERIC(24,12), 0.66)) NotPrecisionError
DECLARE #V SQL_VARIANT
SET #V = 0.55*(287.61 / 0.66)
SELECT
Value = #V
,[TYPE] = CONVERT(SYSNAME, sql_variant_property(#V, 'BaseType')) + '(' +
CONVERT(VARCHAR(10), sql_variant_property(#V, 'Precision')) + ',' +
CONVERT(VARCHAR(10), sql_variant_property(#V, 'Scale')) + ')'
SET #V = 0.55 * (CONVERT(NUMERIC(24,14), 287.61) / CONVERT(NUMERIC(24,14), 0.66))
SELECT
Value = #V
,[TYPE] = CONVERT(SYSNAME, sql_variant_property(#V, 'BaseType')) + '(' +
CONVERT(VARCHAR(10), sql_variant_property(#V, 'Precision')) + ',' +
CONVERT(VARCHAR(10), sql_variant_property(#V, 'Scale')) + ')'
}
RESULTS
PrecisionError NotPrecisionError
239.67499985 239.6750000000000
Value TYPE
239.67499985 numeric(14,8)
Value TYPE
239.6750000000000 numeric(38,13)

Add a random number between 30 and 300 to an existing field

I have looked on the net as well as here but can't find an answer to the following MySQL question. I'm looking to replace the value of an existing field with a query that has a random number between 30 and 300.
Reason was because I've moved galleries and had 250,000,000 views on my images and there have been lost with the migration and a lot of my members are upset that they have lost views....
UPDATE the_table SET the_field = the_field + FLOOR(RAND() * (270 + 1)) + 30
Use RAND()
UPDATE table
SET field = FLOOR(30 + (RAND() * 270));
WHERE foo = 'bar'
I think this will do the trick:
UPDATE table SET field = ROUND(30 + (RAND() * 270)) WHERE id =1;