Same query but different result when ran in different database servers - sql

I have a local environment running MariaDB server version 10.2.14 and a production environment running MariaDB server version 10.1.40.
When running a calculation based on the haversine formula to calculate distance between 2 geolocations, my local environment returns 0 as the result, but the prod environment returns null.
A sample query is below:
select (acos(cos(radians((41.480473))) * cos(radians(41.480473)) *
cos(radians((-81.630990)) - radians(-81.630990)) +
sin(radians((41.480473))) * sin(radians(41.480473))) * 3958.755
) as distance
The result should be zero because it is essentially trying to get the distance between 2 locations with the same geolocation info.
Can anyone shed some light why my prod environment is giving me a null value when running the above sample instead of zero?

The difference comes from rounding errors for the outmost acos-function. The other system may give you exactly 1 while the other system may give you bit over 1 due to rounding errors. Valid parameters for acos are from 1 is 0 and acos value > 1 is NULL.
You can use ROUND before the outmost acos and make the calculation a function for easier use:
create function f_distance_in_miles(
in_lat1 decimal(9,7),
in_long1 decimal(9,7),
in_lat2 decimal(9,7),
in_long2 decimal(9,7)
)
returns float
deterministic
begin
declare v_i real;
set v_i = cos(radians(in_lat1)) * cos(radians(in_lat2)) * cos(radians(in_long1)
- radians(in_long2))
+ sin(radians(in_lat1)) * sin(radians(in_lat2));
if (v_i<-1) then
set v_i = -1;
elseif (v_i>1) then
set v_i = 1;
end if;
return acos(v_i)*3958.755;
end
And then use it like:
select f_distance_in_miles(41.480473, -81.630990, 41.480473, -81.630990)

Related

Fractional exponent POWER function error ("An invalid floating point operation occurred.")

sales_3Y
0.521
1.282
0.71
1.513
1.502
2357
1.291
1.393
0
0
2.009
I need to calculate the 1/nth root of the column.
Select POWER(sales_3YR,(1.0/3))
Error - An invalid floating point operation occurred.
This suggests that POWER(x,n) for fractional n is implemented internally as exp(n*log(x)), which would fail for all x<0. You can work around it for the special cases of an odd n-th root with a user-defined function like:
create or alter function dbo.CubeRoot(#x float)
returns float
as
begin
if #x = 0 return 0;
return sign(#x) * exp(log(abs(#x))/3.0)
end
and
select dbo.CubeRoot(-8)
outputs
-2

Percentage of two values returning NULL

Same as yesterdays question which has been answered successfully but different problem. I have two values, 1 and 0 for which I need to calculate the percent change. Based on this website http://www.percent-change.com/index.php?y1=1&y2=0 the percent change between 1 and 0 is -100%. Based on the suggested formula which is (((y2- y1))/ y1) my code looks like this.
DefinedYearVSPriorYearIndividual = ((( CTEDefinedYear.IndividualCases - CTEPreviousYear.IndividualCasesLastYear ))
/ ( CTEPreviousYear.IndividualCasesLastYear ) ) * 100
which returns NULL.
The two numbers are
CTEDefinedYear.IndividualCases = 1
CTEPreviousYear.IndividualCasesLastYear = 0
The desired result should be -100%.
Can anybody see what I'm doing wrong?
Here is the answer.
Declare #y1 as int =1;
Declare #y2 as int =0;
select (((#y2- #y1))/ #y1)*100
Output is -100. You missed the *100 part.
In your case, You switched variables. attached formula is right one.
select ((0 - 1) / 1)*100;
But you used select ((1 - 0) / 0)*100;
so, you will get an error:
Msg 8134, Level 16, State 1, Line 1
Divide by zero error encountered.
You have to handle 0 in the division side, with CASE logic, to avoid divide by zero error.
DECLARE #CTEDefinedYear_IndividualCases INT = 1
DECLARE #CTEPreviousYear_IndividualCasesLastYear INT = 0
SELECT ((#CTEDefinedYear_IndividualCases - #CTEPreviousYear_IndividualCasesLastYear) / (CASE WHEN #CTEPreviousYear_IndividualCasesLastYear = 0 THEN 1 ELSE #CTEPreviousYear_IndividualCasesLastYear END)) * 100
Got it to work with this code.
DefinedYearVSPriorYearIndividual = ISNULL(100.0 *
(ISNULL(CTEDefinedYear.IndividualCases,0)
- ISNULL(CTEPreviousYear.IndividualCasesLastYear,0))
/ NULLIF(CTEPreviousYear.IndividualCasesLastYear,0),0)

Effective way how to handle application settings in PL/pgSQL functions

Consider following situation: I have PL/pgSQL function which checks, If given auditor has some prerequisites for QS Auditor function. Thresholds of this prerequisites are defined in separate table quasar_settings. Every time, If is the function called, is executed SELECT which retrieves these prerequisites. This is quite inefficient, because this SELECT is called for every row. This quasar_settings table contains only one row. Is there any other more effective solution (global variable, caching, etc)?
Table quasar_settings has only one row.
Using PostgreSQL 9.3
PL/pgSQL function
CREATE OR REPLACE FUNCTION qs_auditor_training_auditing(auditor quasar_auditor) RETURNS boolean AS $$
DECLARE
settings quasar_settings%ROWTYPE;
BEGIN
SELECT s INTO settings
FROM quasar_settings s LIMIT 1;
RETURN auditor.nb1023_procedures_hours >= settings.qs_auditor_nb1023_procedures AND
-- MD Training
auditor.mdd_hours + auditor.ivd_hours >= settings.qs_auditor_md_training AND
-- ISO 9001 Trainig
(
auditor.is_aproved_for_iso13485 OR
(auditor.is_aproved_for_iso9001 AND auditor.iso13485_hours >= settings.qs_auditor_iso13485_training) OR
(auditor.iso13485_hours + auditor.iso9001_hours >= settings.qs_auditor_class_room_training)
);
END;
$$ LANGUAGE plpgsql;
Example of usage:
SELECT auditor.id, qs_auditor_training_auditing(auditor) FROM quasar_auditor auditor;
Do a cross join to the settings table in instead of calling the function at every row
select
a.id,
a.nb1023_procedures_hours >= s.qs_auditor_nb1023_procedures and
-- md training
a.mdd_hours + a.ivd_hours >= s.qs_auditor_md_training and
-- iso 9001 trainig
(
a.is_aproved_for_iso13485 or
(
a.is_aproved_for_iso9001 and
a.iso13485_hours >= s.qs_auditor_iso13485_training
) or
(a.iso13485_hours + a.iso9001_hours >= s.qs_auditor_class_room_training)
)
from
quasar_auditor a
cross join
quasar_settings s

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)

Error converting data type varchar to numeric CAST not working

I know this has been beaten like a dead horse. However no matter how I slice it, cast it or convert it I have the same issue.
Error converting data type varchar to numeric.
SELECT property_id, property_case_number, property_address, property_city,
property_state, property_zip, property_lon, property_lat
FROM property
WHERE (property_active = 1)
AND
(property_county = (SELECT property_county FROM property AS property_1
WHERE (property_id = 9165)))
AND
(property_id <> 9165)
AND
property_lon IS NOT Null
AND
property_lat IS NOT Null
AND
dbo.LatLonRadiusDistance(
CONVERT(DECIMAL(15,12),(select property_lat from property where property_id = 9165)),
CONVERT(DECIMAL(15,12),(select property_lon from property where property_id = 9165)),
property_lat,property_lon) <= '5'
I run into this issue as soon as I add dbo.LatLonRadiusDistance at the end.
dbo.LatLonRadiusDistance compares lat & lon distance in miles.
FUNCTION [dbo].[LatLonRadiusDistance]
(
#lat1Degrees decimal(15,12),
#lon1Degrees decimal(15,12),
#lat2Degrees decimal(15,12),
#lon2Degrees decimal(15,12)
)
RETURNS decimal(9,4)
AS
BEGIN
DECLARE #earthSphereRadiusKilometers as decimal(10,6)
DECLARE #kilometerConversionToMilesFactor as decimal(7,6)
SELECT #earthSphereRadiusKilometers = 6366.707019
SELECT #kilometerConversionToMilesFactor = .621371
-- convert degrees to radians
DECLARE #lat1Radians decimal(15,12)
DECLARE #lon1Radians decimal(15,12)
DECLARE #lat2Radians decimal(15,12)
DECLARE #lon2Radians decimal(15,12)
SELECT #lat1Radians = (#lat1Degrees / 180) * PI()
SELECT #lon1Radians = (#lon1Degrees / 180) * PI()
SELECT #lat2Radians = (#lat2Degrees / 180) * PI()
SELECT #lon2Radians = (#lon2Degrees / 180) * PI()
-- formula for distance from [lat1,lon1] to [lat2,lon2]
RETURN ROUND(2 * ASIN(SQRT(POWER(SIN((#lat1Radians - #lat2Radians) / 2) ,2)
+ COS(#lat1Radians) * COS(#lat2Radians) * POWER(SIN((#lon1Radians - #lon2Radians) / 2), 2)))
* (#earthSphereRadiusKilometers * #kilometerConversionToMilesFactor), 4)
END
I'm sure it's something to do with
(select property_lat from property where property_id = 9165)
But no matter how I cast or convert it doesn't change things.
And if I run the function by itself it doesn't give an error.
Anyone have any insights?
here is a sample row
8462 023-125514 15886 W MOHAVE ST GOODYEAR AZ 85338-0000 -112.400297000000 33.429041000000
property_lat & property_lon are varchar(50)
Most likely you are expecting boolean short circuit to rescue the order of evaluating your WHERE clause. This is a known fallacy: boolean operator short circuit is not guaranteed in SQL. See On SQL Server boolean operator short-circuit for a discussion and proof that boolean short circuit can be skipped by query optimizer. Similar topic is T-SQL functions do no imply a certain order of execution. The gist of it is that SQL is a declarative language, not an imperative one.
In your case probably your cast and converts will be called for properties with IDs different from property_id = 9165 and property_active=1 and may attempt to cast string values that are not numerics to a numeric, hence the exception you see. Is difficult to give a precise diagnosis since so much information is missing from your problem description (like the exact definition of all object involved, including all tables, indexes, column types etc).
Your best avenue is to upgrade to SQL Server 2008 and use the built in geography type which has built-in support for STDistance:
This is a close approximate to the geodesic distance. The deviation of
STDistance() on common earth models from the exact geodesic distance
is no more than .25%.
After playing with the query I got it working.
SELECT [property_id], [property_case_number], [property_address], [property_city],
[property_state], [property_zip], [property_lon],
[property_lat]
FROM property
WHERE ([property_active] = 1)
AND
([property_county] = (SELECT b.property_county FROM property b WHERE (b.property_id = #prop_id)))
AND
([property_id] <> #prop_id)
AND
[property_lon] IS NOT Null
AND
[property_lat] IS NOT Null
AND
dbo.LatLonRadiusDistance(
(SELECT c.property_lat FROM property c WHERE (c.property_id = #prop_id)),
(SELECT d.property_lon FROM property d WHERE (d.property_id = #prop_id)),
CAST([property_lat] as FLOAT),
CAST([property_lon] as FLOAT)) <= 5
adding the [] seems to have skirted the issue I was having.