CHECK Constraint for SUM failing on double precision column - sql

I'm trying to understand a failure on my CHECK constraint in SQL Server 2008 R2 (the same problem occurs on SQL Server 2012).
My sql command just update the amount by 126.3 on two columns and the constraint checks if the sum of two columns match a third column.
Below are the steps to reproduce the problem:
CREATE TABLE FailedCheck ( item VARCHAR(10), qty_total DOUBLE PRECISION, qty_type1 DOUBLE PRECISION, qty_type2 DOUBLE PRECISION )
ALTER TABLE FailedCheck ADD CONSTRAINT TotalSum CHECK(qty_total = (qty_type1 + qty_type2));
INSERT INTO FailedCheck VALUES ('Item 2', 101.66, 91.44, 10.22);
UPDATE FailedCheck SET qty_total = qty_total + 126.3, qty_type1 = qty_type1 + 126.3
Column qty_total must contain the sum of (qty_type1 and qty_type2). All columns are 'Double precision'. If I change the value from 126.3 to 126, it works, I've tested other values (int and double) and couldn't understand why sometimes it works and sometimes doesn't.
What's wrong with my CHECK constraint ?
PS: Sorry for my english, it's not my primary language.

You decided for a floating point data type which only holds an approximate value - quite precise, but only up to an extent. 1.3 may well be stored as 1.299999999999998 or something along the lines. So the sum for the approximate values of 91.44 and 10.22 may happen to be exactly the approximate value for 101.66, but may also be very slightly different.
Never compare floating point values with the equal sign (=).
And better don't use floting point types in the first place, if not really, really needed. Use DECIMAL instead.

Related

Create table with numeric column

CREATE TABLE SampleMath
(m NUMERIC (10,3),
n INTEGER,
p INTEGER);
Question: why there is a space between NUMERIC and (10,3).
At first, I thought (10,3) is a column constraint. However, it doesn't seem to be correct as common constraints are NOT NULL, UNIQUE as listed here.
Then I thought that it could be the property (precision, scale) for the datatype NUMERIC as described in the documentation. In that case, I think there should not be a space between NUMERIC and (10,3). I also tried to delete the space in between and it seems the code still works. In my understanding, if it is the property, there should not be a space, which makes me really confused.
Any help to clarify this would be really appreciated. Thanks for your help in advance.
NUMERIC (10,3) is a datatype. It can store a number with a total of 10 digits (that's called precision), including 3 decimal (aka scale, ie the count of digits at the right of the decimal point). So basically the biggest number that it can store is 9999999.999.
The space between NUMERIC and the definition of its scale and precision is not meaningful. Even a new line would be OK, like:
CREATE TABLE SampleMath
(m NUMERIC
(10,3),
n INTEGER,
p INTEGER);

Placeholder for Decimal Column in SQL

I am new to SQL so this question is likely simple and easy to answer.
I am creating a temp table in which I want a blank column to be filled later with decimal values.
Can I use as a placeholder in my SELECT statement to indicate that I want decimal values to fill the column?
For columns that I will fill with integer values, I am using the following code:
SELECT 0 AS ColumnName
I do not believe this will work for the column that I want filled with decimal values, as I believe the 0 indicates integer values instead. Is there something that I can use instead of the 0?
Any help would be appreciated!
SQL will perform implicit conversion of some types when it knows there is no risk of data loss. In your case, int can safely convert to decimal, because there's no way to corrupt your data. 0 is 0.0 as far as SQL is concerned.
The opposite would not be true, as casting from decimal to int would lose the decimal part. Therefore SQL would not implicitly cast the opposite.
Your query is good as is.
Using a 0 in a float field will not cause truncation on the record since it contains less precision than a float, so it is safe to do this and implies a 0.0
You could also DEFAULT the field to 0.0 in the table declaration so it will be 0.0 until updated. But not required.
Ex: CREATE TABLE table
( col1 FLOAT(size,d) DEFAULT 0.0 )

How do you update float data type in SQL column to hold numbers with upto 20 scale

I have a SQL table with a column having float data type with default precision.
I want this column to hold data with upto 20 digits after the decimal point.
How do I update existing column to do this?
SQL Server: The literal answer here would be "you cannot". If you look at the documentation, you'll see that the float type only supports two precisions - either 7 or 15 digits.
Answers provided so far all seem to be advocating a switch to a different datatype (decimal) but don't highlight that this comes with its own drawbacks - notably that the decimal type supports a far smaller range. float can support a range from - 1.79E+308 to -2.23E-308, 0 and 2.23E-308 to 1.79E+308 whereas decimal only supports a range of ~ -10^38 to 10^38 (and that, only if you are using a 0 scale). If using a scale of 20 then the range is limited to ~ -10^18 to 10^18.
It depends on how much value You want to store to the left of the decimal point. Lets say only number from 0-9 and 20 digits on the right.
Alter table tablename altercolumn columnname(21,20)
try it
ALTER TABLE MyTable ALTER COLUMN MyColumndecimal decimal(38,20)
decimal(precision, scale)
its mean left side = 18, right side = 20
Just put decimal(precision, scale), replacing the precision and scale with your desired values.
ALTER TABLE TableName ALTER COLUMN ColumnName decimal(24,20)

SQL Server: what to do when a decimal isn't really a decimal?

I have a situation where a decimal figure isn't truly a decimal, it is a sub-count of sorts. For example, when an prescription for medication is filled it is given an Rx number (lets say 345673). That number will stay with the prescription throughout any refills and the refills append a .1, .2 etc. So, over the life of that Rx number you could end up with 345673, 345673.1, 345673.2... ongoing. The problem is when you hit .10, .20, .30 etc. In decimal form those records are the same as .1, .2, .3.
Is there any way to track these numbers and support the trailing zeros without having use VARCHAR etc? This is the primary key column and I'm not crazy about using varchar on a Pk (is that old fashioned?)
Any and all suggestions/help is appreciated.
Edit to add: I should have explained why we can't use separate columns for this. This data originates elsewhere and is merged into our database from a bulk import operation. The merge is perpetual since much of the data is altered at the origin and combined on the next bulk import. The Rx number has to match exactly to perform the bulk import / merge.
I would recommend using a composite primary key. Add a second column, perhaps called refill, and use that for your incremental values. That way both columns would be integers and no need to use varchar for your primary key.
You may need to use a trigger to maintain the value as identity fields don't work with groupings.
Convert the decimal to varchar using either CONVERT or CAST. Then you can use character/string functions such as LEFT(), SUBSTRING(), and REPLACE().
The other option is to parse out the decimal using math:
DECLARE #RxValue decimal(18,4) = 100.234
SELECT #RxValue [Rx Number]
,#RxValue - CONVERT(int,#RxValue) [Refill Count Decimal]
,LEN(CONVERT(float,(#RxValue - CONVERT(int,#RxValue))))-2 [After Decimal Length]
,POWER(10, LEN(#RxValue - CONVERT(int,#RxValue))-2) [Calc multiplier for Refill Count]
,CONVERT(int,#RxValue) [Rx ID]
,CONVERT(int,(#RxValue - CONVERT(int,#RxValue)) * POWER(10, LEN(CONVERT(float,(#RxValue - CONVERT(int,#RxValue))))-2)) [Refill Count]
The last two columns in the above select are your two desired columns. All that is above is my attempt to "show my work" so you can see what I was thinking. The "-2" is to to remove the "0." out of the LEN() function's result. The POWER() function takes the 10 and raises it to the power of the decimal result.
You should only need to replace all references to #RxValue with the column name of your "Rx number" to make it work. If you want to play around with it, you can change the values in #RxValue to whatever you want and make sure the result is what you expect. Be sure you change #RxValue's data type to match yours.
You can, of course keep your "Rx number" as the PK if that is the situation it is in. You'll just be adding a couple of derived columns: [Rx ID] and [Refill Count].
Please leave a comment if you have a question.

Multiplication with NULL and empty column values in SQL

This was my Interview Question
there are two columns called Length and Breadth in Area table
Length Breadth Length*Breadth
20 NULL ?
30 ?
21.2 1 ?
I tried running the same question on MYSQL while inserting,To insert an empty value I tried the below query . Am I missing anything while inserting empty values in MYSQL.
insert into test.new_table values (30,);
Answers: With Null,Result is Null.
With float and int multiplication result is float
As per your question the expected results would be as below.
SELECT LENGTH,BREADTH,LENGTH*BREADTH AS CALC_AREA FROM AREA;
LENGTH BREADTH CALC_AREA
20
30 0 0
21.2 1 21.2
For any(first) record in SQL SERVER if you do computation with NULL the answer would be NULL.
For any(second) record in SQL SERVER, if you do product computation between a non-empty value and an empty value the result would be zero as empty value is treated as zero.
For any(third) record in SQL SERVER, if you do computation between two non-empty data type values the answer would be a NON-EMPTY value.
Check SQL Fiddle for reference - http://sqlfiddle.com/#!3/f250a/1
That blank Breath (second row) cannot happen unless Breath is VARCHAR. Assuming that, the answers will be:
NULL (since NULL times anything is NULL)
Throws error (since an empty string is not a number. In Sql Server, the error is "Error converting data type varchar to numeric.")
21.20 (since in Sql Server, for example, conversion to a numeric type is automatic, so SELECT 21.2 * '1' returns 21.20).
Assuming that Length and Breadth are numerical types of some kind the second record does not contain possible values — Breadth must be either 0 or NULL.
In any event, any mathematical operation in SQL involving a NULL value will return the value NULL, indicating that the expression cannot be evaluated. The answer are NULL, impossible, and 21.2.
The product of any value and NULL is NULL. This is called "NULL propagation" if you want to Google it. To score points in an interview, you might want to mention that NULL isn't a value; it's a special marker.
The fact that the column Breadth has one entry "NULL" and one entry that's blank (on the second row) is misleading. A numeric column that doesn't have a value in a particular row means that row is NULL. So the second column should also show "NULL".
The answer to the third row, 21.2 * 1, depends on the data type of the column "Length*Breadth". If it's a data type like float, double, or numberic(16,2), the answer is 21.2. If it's an integer column (integer, long, etc.), the answer is 21.
A more snarky answer might be "There's no answer. The string "Length*Breadth" isn't a legal SQL column name."
In standard SQL they would all generate errors because you are comparing values (or nulls) of different types:
CAST ( 20 AS FLOAT ) * CAST ( NULL AS INTEGER ) -- mismatched types error
CAST ( '' AS INTEGER ) -- type conversion error
CAST ( AS INTEGER ) -- type conversion error
CAST ( 21.2 AS FLOAT ) * CAST ( 2 AS INTEGER ) -- mismatched types error
On the other hand, most SQL product would implicitly cast values when comparing values (or nulls) of different types according to type precedence e.g. comparing float value to an integer value would in effect cast the integer to float and result in a float. At the product level, the most interesting question is what happens when you compare a null of type integer with a value (or even a null) of type float...
...but, frankly, not terribly interesting. In an interview you are presented with a framework (in the form of questions asked of you) on which to present your knowledge, skills and experience. The 'answer' here is to discuss nulls (e.g. point out that nulls are tricky to define and behave in unintuitive ways, which leads to frequent bugs and a desire to avoid nulls entirely, etc) and whether implicit casting is a good thing.