Calculate percentage by using column value - sql

I got a table with the below data in it.
all I need to do is a query the calculate 10% of the damage so I have done the below:
SELECT damage = (damage * 10) /100
,[Peril_Code]
FROM [LocExposure].[dbo].[myTable]
and this return the below result:
[Damage] column is a float .
I cannot figure out why it returns the wrong calculation.

Your output is correct. No error. Please check the result 2.44E-06 where your actual value was 2.44E-05.
So, there is no error.
UPDATE:
For avoiding scientific notation, you can go through the following post
convert float into varchar in SQL server without scientific notation

DECLARE #Damage TABLE
(
Damage FLOAT,
PerilCode CHAR(1)
)
INSERT INTO #Damage VALUES
(2.44253351044103E-05 , 'T'),
(0.000125444785042888 , 'T'),
(0.00015258112714104 , 'T'),
(0.000238995871781784 , 'T'),
(0.000267978447740977 , 'T')
SELECT Damage, Damage * 0.1 [10%], PerilCode
FROM #Damage
Output
Damage 10% PerilCode
---------------------------------------------------------
2,44253351044103E-05 2,44253351044103E-06 T
0,000125444785042888 1,25444785042888E-05 T
0,00015258112714104 1,5258112714104E-05 T
0,000238995871781784 2,38995871781784E-05 T
0,000267978447740977 2,67978447740977E-05 T

Related

HANA database - Injection of a Table User Defined Function into a lateral join

In short
On an HANA database, I have set a Table User Defined Function which returns a 1-row table with 3 columns ;
I would like to use it inside a lateral join but so far my attempts have been to no avail.
The problem
Let's say we have the following dummy Table User Defined Function :
CREATE OR REPLACE FUNCTION PBANALYST. F__ITEM_MBEW(
IN
p_str_MATNR NVARCHAR(18)
, p_str_BWKEY NVARCHAR(02)
, p_str_VALDATE NVARCHAR(08)
)
RETURNS
TABLE(
VALDATE NVARCHAR(08)
, LBKUM INTEGER
, VERPR DECIMAL
, STPRS DECIMAL
)
LANGUAGE SQLSCRIPT
SQL SECURITY INVOKER
AS
BEGIN
RETURN
SELECT
'20220928' AS VALDATE
, 10 AS LBKUM
, 5.3 AS VERPR
, 10.5 AS STPRS
FROM DUMMY
;
END;
It works fine on its own.
But when I try to inject it inside a lateral join, I get an error :
DO
BEGIN
tbl_MATNR_LIST =
SELECT '000000000000824151' AS MATNR , '92' AS div , '20220715' AS VALDATE FROM dummy
;
SELECT
tbl_MATNR_LIST. *
FROM :tbl_MATNR_LIST tbl_MATNR_LIST ,
LATERAL(
SELECT *
FROM F__ITEM_MBEW(
'000000000000824151'
, '92'
, '20220715'
)
) MBEW
;
END;
DataSource.Error : ODBC: ERROR [S1000] [SAP AG][LIBODBCHDB DLL][HDBODBC] General error;318 decimal precision specifier is out of range: -1: (1 to 38)
How can I fix it?
Thank you for your help.
The error message
DataSource.Error : ODBC: ERROR [S1000] [SAP AG][LIBODBCHDB DLL][HDBODBC] General error;318 decimal precision specifier is out of range: -1: (1 to 38)
can easily be fixed by specifying the decimal data type lengths in the user defined function:
TABLE(
VALDATE NVARCHAR(08)
, LBKUM INTEGER
, VERPR DECIMAL (10, 2)
, STPRS DECIMAL (10, 2)
)
Unfortunately, that does not help with the overall goal: to use the UDF in the LATERAL join in a meaningful way.
What does work is something like this:
DO
BEGIN
tbl_MATNR_LIST =
SELECT '000000000000824151' AS MATNR
, '92' AS div
, '20220715' AS VALDATE
FROM dummy;
SELECT
tml. *
FROM :tbl_MATNR_LIST tml
CROSS JOIN LATERAL(
F__ITEM_MBEW( '000000000000824151'
, '92'
, '20220715')
) MBEW;
END;
This does return the result, but obviously does not use any "lateral" references.
If one tries to make "grab stuff side-ways" this is the result:
[...]
CROSS JOIN LATERAL(
F__ITEM_MBEW( '000000000000824151'
, tml.div
, '20220715')
) MBEW;
[...]
SQL Error [7] [HY000]: SAP DBTech JDBC: [7] (at 292): feature not supported: non-field expression with LATERAL: line 13 col 29 (at pos 292)
At this point, I would guess that this way of using the LATERAL JOIN is just not supported (on version 2.00.057).

Could someone help me understand the ambiguity here in Postgres?

So I've been trying out PG for a few days, specifically through NpgSQL in dotnet core, but I don't believe that is relevant to my question. I've been writing a couple of update functions. The first one was easy:
CREATE OR REPLACE FUNCTION "Api"."UpdateExpenseReceipt" ( "vReceiptID" UUID , "vTotal" DOUBLE PRECISION , "vTaxPercent" DOUBLE PRECISION , "vShippingCost" DOUBLE PRECISION , "vReceiptDate" TIMESTAMP , "vReference" VARCHAR , "vCurrentToken" UUID )
RETURNS TABLE ( ReceiptID UUID , Total DOUBLE PRECISION , TaxPercent DOUBLE PRECISION , ShippingCost DOUBLE PRECISION , Reference VARCHAR(96) , ReceiptDate TIMESTAMP )
LANGUAGE PLPGSQL
AS $$
DECLARE "iValidReceipt" INTEGER;
DECLARE "iValidUser" INTEGER;
BEGIN
"iValidReceipt" := ( SELECT COUNT("ReceiptID") FROM "Users"."ExpenseReceipt" WHERE "ReceiptID" = "vReceiptID" );
"iValidUser" := ( SELECT COUNT("AccountID") FROM "Users"."Account" WHERE "CurrentToken" = "vCurrentToken" LIMIT 1 );
IF "iValidUser" = 0 THEN
RAISE 'Error' USING ERRCODE = '10001';
END IF;
IF "iValidReceipt" > 0 THEN
UPDATE "Users"."ExpenseReceipt" SET
"Total" = COALESCE( "vTotal" , "Total" )
, "TaxPercent" = COALESCE( "vTaxPercent" , "TaxPercent" )
, "ShippingCost" = COALESCE( "vShippingCost" , "ShippingCost" )
, "Reference" = COALESCE( CAST( "vReference" AS VARCHAR ) , "Reference" )
, "ReceiptDate" = COALESCE( "vReceiptDate" , "ReceiptDate" )
, "EditDate" = current_timestamp at time zone 'utc'
WHERE "ReceiptID" = "vReceiptID";
RETURN QUERY
SELECT
"ReceiptID"
, "Total"
, "TaxPercent"
, "ShippingCost"
, "Reference"
, "ReceiptDate"
FROM "Users"."ExpenseReceipt"
WHERE "ReceiptID" = "vReceiptID";
ELSE
RAISE 'Error' USING ERRCODE = '10101';
END IF;
END; $$
--I'll include the table itself in case its relevant
CREATE TABLE "Users"."ExpenseReceipt"
(
"ReceiptID" UUID NOT NULL DEFAULT uuid_generate_v4(),
"AccountID" UUID NOT NULL ,
"Total" DOUBLE PRECISION NOT NULL ,
"TaxPercent" DOUBLE PRECISION DEFAULT 0.0 ,
"ShippingCost" DOUBLE PRECISION DEFAULT 0.0 ,
"Reference" VARCHAR(96) ,
"ReceiptDate" TIMESTAMP ,
"EditDate" TIMESTAMP DEFAULT ( current_timestamp at time zone 'utc' )
);
Easy update function, uses coalesce to not update the value if the API call doesn't set them. Everything works fine (after dealing through the numerous naming issues I've run into with postgres, and there I think NpgSQL is relevant). I know how to get it right. I made a second one, basically exactly the same:
CREATE OR REPLACE FUNCTION "Api"."UpdateSupplyItem" ( "vItemID" UUID , "vDescription" VARCHAR , "vSize" VARCHAR , "vNetCost" DOUBLE PRECISION , "vPackageQuantity" DOUBLE PRECISION , "vNetWeight" DOUBLE PRECISION , "vCurrentToken" UUID )
RETURNS TABLE ( "ItemID" UUID , "Description" VARCHAR , "Size" VARCHAR , "NetCost" DOUBLE PRECISION , "PackageQuantity" DOUBLE PRECISION , "NetWeight" DOUBLE PRECISION )
LANGUAGE PLPGSQL
AS $$
DECLARE "iValidItem" INTEGER;
DECLARE "iValidUser" INTEGER;
BEGIN
"iValidItem" := ( SELECT COUNT(usi."ItemID") FROM "Users"."SupplyItem" usi WHERE usi."ItemID" = "vItemID" );
"iValidUser" := ( SELECT COUNT("AccountID") FROM "Users"."Account" WHERE "CurrentToken" = "vCurrentToken" LIMIT 1 );
IF "iValidUser" = 0 THEN
RAISE 'Error' USING ERRCODE = '10001';
END IF;
IF "iValidItem" > 0 THEN
UPDATE "Users"."SupplyItem"
SET
"Description" = COALESCE( "vDescription" , "Users"."SupplyItem"."Description" )
, "Size" = COALESCE( "vSize" , "Users"."SupplyItem"."Size" )
, "NetCost" = COALESCE( "vNetCost" , "Users"."SupplyItem"."NetCost" )
, "PackageQuantity" = COALESCE( "vPackageQuantity" , "Users"."SupplyItem"."PackageQuantity")
, "NetWeight" = COALESCE( "vNetWeight" , "Users"."SupplyItem"."NetWeight" )
WHERE "Users"."SupplyItem"."ItemID" = "vItemID";
RETURN QUERY SELECT usi."ItemID" , usi."Description" , usi."Size" , usi."NetCost" , usi."PackageQuantity" , usi."NetWeight" FROM "Users"."SupplyItem" usi WHERE usi."ItemID" = "vItemID" LIMIT 1;
ELSE
RAISE 'Error' USING ERRCODE = '10401';
END IF;
END; $$
--Again, the table in case it helps
CREATE TABLE "Users"."SupplyItem"
(
"ItemID" UUID NOT NULL DEFAULT uuid_generate_v4() ,
"AccountID" UUID NOT NULL ,
"Description" VARCHAR ,
"Size" VARCHAR ,
"NetCost" DOUBLE PRECISION ,
"PackageQuantity" DOUBLE PRECISION ,
"NetWeight" DOUBLE PRECISION
);
but it's quite different. You can see clearly that I've had to fully qualify the right hand side of every equals (where the earlier function had no need). I get ambiguity all the way down the update statement. It starts at ItemID, then Description, then Size... every attribute I think. First thing I did was alias the table
UPDATE usi [...] FROM "Users"."SupplyItem" usi
but that failed because you can't do short aliasing in an UPDATE in PG (relation usi."[...]" does not exist") which actually kind of sucks. I only figured out that it needed to be fully qualified when someone asked a similar question and the answer was "It must be a quirk of RETURNS TABLE."
So why is my second update "a quirk" but my first update works perfectly? I've had a tough time with PG (and I'm not a slouch), but having two functions that seem identical having entirely different results (at runtime no less) makes me uncomfortable. I'm posting here because I know the two functions must be markedly different; 99.9% of the time, there is no such thing as a "quirk." There is something I need to understand to work around to avoid in the future. What is the "gotcha" that I've missed in the second UPDATE function?
The problem is that you have a function variable "Size" (an OUT parameter defined in the RETURNS TABLE clause) and a column "Size" in "Users"."SupplyItem". So you have to qualify the reference to indicate what you mean.
I recommend using an alias for simplicity:
UPDATE "Users"."SupplyItem" AS si
SET "Size" = COALESCE("vSize" , si."Size")
...
There is no such ambiguity in your first example, because you didn't double quote the parameter TaxPercent, so it gets case folded to taxpercent and is different from the column "TaxPercent".

Arithmetic overflow error converting expression to data type float

Working on an analysis of bonds. I have attempted to make a payment function that replicates the PMT function of excel. For the bonds, the "Cusip" is their identifier, their "PASS THRU RATE" is their annual interest rate, the "ORIGINAL WA MATURITY" is the total number of periods, and the "ORIGINAL BALANCE" is the original face value of the bond.
The equation for calculating a monthly payment by paper is:
M=[OF(i(1+i)^n)]/[(1+i)^(n-1)]
M=Monthly payment
OF=Original Face
i=annual interest rate/12
n=number of periods
I have a table with all the columns needed for this function, as well as different tables for different months that I will try and use this for. This is what I have so far, creating the function and trying to fix for data types:
if object_id('dbo.PMT') > 0
drop function dbo.PMT
go
create function dbo.PMT(#rate numeric(15,9), #periods smallint, #principal numeric(20,2) )
returns numeric (38,9)
as
begin
declare #pmt numeric (38,9)
select #pmt = #principal
/ (power(1+#rate,#periods)-1)
* (#rate*power(1+#rate,#periods))
return #pmt
end
go
drop function dbo.PMT
go
create function dbo.PMT
(
#rate float,
#periods smallint,
#principal numeric(20,2)
)
returns numeric (38,9)
as
begin
declare #pmt numeric (38,9)
declare #WK_periods float,
#WK_principal float,
#wk_One float,
#WK_power float
select #WK_periods = #periods,
#WK_principal = #principal,
#WK_One = 1
select #pmt =
round(
( #WK_principal * (#rate*power(#WK_One+#rate,#WK_periods)))
/ (power(#WK_One+#rate,#WK_periods)-#WK_One)
,9)
return #pmt
end
go
select ALL [CUSIP NUMBER]
,[PASS THRU RATE]
,[ORIGINAL WA MATURITY]
,[ORIGINAL BALANCE],
dbo.pmt((mbs012013.[PASS THRU RATE]),mbs012013.[ORIGINAL WA MATURITY],mbs012013.[ORIGINAL BALANCE])
FROM
[MBS_STATS].[dbo].[mbs012013]
However, I receive
(502882 row(s) affected)
Msg 8115, Level 16, State 2, Line 2
Arithmetic overflow error converting expression to data type float.
when I attempt to execute it. I cannot figure out what is causing this. Any help would be great!
In the line below, you have #WK_principal as a FLOAT, and you're assigning the value of #principal which is a NUMERIC(20,2).
#WK_principal = #principal,
That seems to be the most likely culprit. We'd need to be able to see your data to help otherwise. Also, I'm not clear on why you're creating the function one way, then dropping it and recreating it differently. Or are you just showing two different attempts?

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.