Precision equal to scale in MS SQL-Server - sql-server

I recently came across a column declaration where the precision equal to scale :
...
[MYCOLUMN] [decimal](5, 5) NULL,
...
According to this documentation this seems correct.
But i don't understand, does it means that the decimal intended to fit in, can only have numbers to the right of the decimal point ?

Yes, it means that only decimals are allowed when inserting into the table. Everything else will be truncated and will most likely generate a "Data will be truncated" warning.
So the possible values for that column are in the following range:
0 - 0.99999
Have a look at this SQLFiddle. If you uncomment the inserting of 1 into the table you will get a truncation error.
Also, in case SQLFiddle ever goes down, here is the code:
create table test (col1 decimal(5,5));
insert into test values (0.12345);
insert into test values (0);
-- insert into test values (1);
select *
from test

Related

How do you use Snowflake RATIO_TO_REPORT to be precise?

I'm having a problem with Snowflake function RATIO_TO_REPORT and it's rounding. It seems like there is default rounding behavior, which causes a sum to be different from 1.
How would you address this issue?
RATIO_TO_REPORT Issue
Cheers,
Joe
Nothing going on here is technically "wrong" - as you've identified, this is a rounding issue. Each of the RATIO_TO_REPORT results is correct, and the sum of the values is also correct.
The best way to get around this is to force RATIO_TO_REPORT to output a more precise number by casting the input to NUMBER rather than INTEGER. In my testing, the below worked well:
-- Create the table for testing
CREATE TABLE R2R_TEST (activity_count integer);
-- Insert the values from the example screenshot.
INSERT INTO R2R_TEST VALUES (210),(11754),(3660),(66);
-- Create the test RATIO_TO_REPORT query with the cast to NUMBER
SELECT RATIO_TO_REPORT(activity_count::number(32,8)) OVER (ORDER BY activity_count) r2r FROM R2R_TEST;
-- Check our work.
with test as (SELECT RATIO_TO_REPORT(activity_count::number(32,8)) OVER (ORDER BY activity_count) r2r FROM R2R_TEST)
SELECT SUM(r2r) FROM test; -- 1.0000000000[...]
You can see that all I've done is cast activity_count to a NUMBER, and this gives the unrounded result.

SSRS: SUM values if contending with NULLS

In SSMS, this is easy with an ISNULL function. So in SSRS, how would I go about getting a SUM value from two columns where one of them has NULLS?
Here's an example of the data:
So if a pro_rate value exists, I want to use it in the aggregation, but if it is NULL, then I need to pull from the cost column. Here's my most recent attempt at the expression:
=iif(isnothing(sum(Fields!pro_rate.Value), sum(Fields!cost.Value), sum(Fields!pro_rate.Value)))
In SQL, I would simply write sum(isnull(pro_rate, cost))(to also ensure it's clear what my result should be) but I keep getting the dreaded #Error in my report.
UPDATE 1: I altered my query a bit to the following and got the #Error.
=sum(iif(isnothing(Fields!pro_rate.Value), Fields!cost.Value, Fields!pro_rate.Value))
After looking at your data and the expressions being used, I came to the conclusion that you are seeing that error message due to a conversion error. It looks like your cost field is an INT datatype as the values all seem to be rounded off to the nearest integer and the pro_rate field appears to be a DECIMAL datatype. This appears to cause a problem when you are attempting to SUM an integer into a decimal without the proper level of precision.
To test this, I created the following simple dataset:
CREATE TABLE #temp (A VARCHAR(2), B INT, C DECIMAL(4,2))
INSERT INTO #temp (A,B,C)
VALUES ('A', '5', '1.7'), ('B', '10', '2.6'), ('C', '9', NULL)
SELECT * FROM #temp
With this data, I was able to test and confirm that my theory was correct and you should be able to fix your #Error by putting a CDEC function around the cost field in the expression provided by Derrick Moeller.
It looks to me like you want to evaluate each row individually and you have your order of operations incorrect.
=sum(iif(isnothing(Fields!pro_rate.Value), Fields!cost.Value, Fields!pro_rate.Value))

SQL Server 2005 decimal field behaviour

Ive create a simple table with a field x of type decimal(28,15)
I run the following query to insert a value:
insert into testtable values (1234567890123)
when I do a select from testtable I, field x has a value of
1234567890123.000000000000000
Is there a way to specify (perhaps in the table design) that sql server should NOT store the zero's after the decimal if its not required? i.e. when doing a select the data will be returned as
1234567890123
Also why does insert into testtable values (12345678901234) give the following error :
Arithmetic overflow error converting numeric to data type numeric.
Also why does insert into testtable values (12345678901234) give the following error :
You specified a total width of 28 with 15 digits after decimal point. That leaves at most 13 digits before decimal point.
Is there a way to specify that sql server should NOT store the zero's after the decimal if its no required?
Strictly speaking, "decimal" is a fixed precision format and thus any number will occupy exactly the same space as any other number (see MSDN).
Try the below query . it works fine ...
select cast(1234567890123.000000000000000 as decimal)

How to query for rows containing <Unable to read data> in a column?

I have a SQL table in which some columns, when viewed in SQL Server Manager, contain <Unable to read data>. Does anyone know how to query for <Unable to read data>? I can individually modify the data in this column with update table set column = NULL where key = 'value', but how can I find whether additional rows exist with this bad data?
I would recommend against replacing the data. There is nothing wrong with it, is just that SSMs cannot display it properly in the Edit panel. The data in the database itself is perfectly fine, from your description.
This script shows the problem:
create table test (id int not null identity(1,1) primary key,
large_value numeric(38,0));
go
insert into test (large_value) values (1);
insert into test (large_value) values (12345678901234567890123456789012345678);
insert into test (large_value) values (1234567890123456789012345678901234567);
insert into test (large_value) values (123456789012345678901234567890123456);
insert into test (large_value) values (12345678901234567890123456789012345);
insert into test (large_value) values (1234567890123456789012345678901234);
insert into test (large_value) values (123456789012345678901234567890123);
insert into test (large_value) values (12345678901234567890123456789012);
insert into test (large_value) values (1234567890123456789012345678901);
insert into test (large_value) values (123456789012345678901234567890);
insert into test (large_value) values (12345678901234567890123456789);
insert into test (large_value) values (NULL);
go
select * from test;
go
The SELECT will work fine, but showing the Edit Top 200 Rows in object explorer will not:
There is a Connect Item for this issue. SSMS 2012 still exhibits the same problem.
If we look at the Numeric and Decimal details we'll see that the problem occurs at a weird boundary, at precision 29 which is actually not a SQL Server boundary (precision 28 is):
Precision Storage bytes
1 - 9 5
10-19 9
20-28 13
29-38 17
If we check the .Net (SSMS is a managed application) decimal precision table we can see quickly where the crux of the issue is: Precision is 28-29 significant digits. So the .Net decimal type cannot map high precision (>29) SQL Server numeric/decimal types.
This will affect not only SSMS display, but your applications as well. Specialized applications like SSIS will use high precisions representation like DT_NUMERIC:
DT_NUMERIC An exact numeric value with a fixed precision and scale.
This data type is a 16-byte unsigned integer with a separate sign, a
scale of 0 - 38, and a maximum precision of 38.
Now back to your problem: you can discover invalid entries by simply looking at the value. Knowing that the C# representation range can accommodate values between approximate (-7.9 x 1028 to 7.9 x 1028) / (100 to 28)` (the range depends on the scale) you can search for values outside the range on each column (the actual values to search between will depend on the column scale). But that begs the question 'what to replace the data with?'.
I would recommend instead using dedicated tools for import export, tools that are capable of handling high precision numeric values. SSIS is the obvious candidate. But even the modest bcp.exe would also fit the bill.
BTW if your values are actually incorrect (ie. true corruption) then I would recommend running DBCC CHECKTABLE (...) WITH DATA_PURITY:
DATA_PURITY
Causes DBCC CHECKDB to check the database for column values that are not valid or out-of-range. For example, DBCC CHECKDB detects
columns with date and time values that are larger than or less than
the acceptable range for the datetime data type; or decimal or
approximate-numeric data type columns with scale or precision values
that are not valid.
For databases created in SQL Server 2005 and later, column-value integrity checks are enabled by default and do not require the
DATA_PURITY option. For databases upgraded from earlier versions of
SQL Server, column-value checks are not enabled by default until DBCC
CHECKDB WITH DATA_PURITY has been run error free on the database.
After this, DBCC CHECKDB checks column-value integrity by default.
Q: How can this issue arise for a datetime column?
use tempdb;
go
create table test(d datetime)
insert into test (d) values (getdate())
select %%physloc%%, * from test;
-- Row is on page 0x9100000001000000
dbcc traceon(3604,-1);
dbcc page(2,1,145,3);
Memory Dump #0x000000003FA1A060
0000000000000000: 10000c00 75f9ff00 6aa00000 010000 ....uùÿ.j .....
Slot 0 Column 1 Offset 0x4 Length 8 Length (physical) 8
dbcc writepage(2,1,145, 100, 8, 0xFFFFFFFFFFFFFFFF)
dbcc checktable('test') with data_purity;
Msg 2570, Level 16, State 3, Line 2 Page (1:145), slot 0 in object ID
837578022, index ID 0, partition ID 2882303763115671552, alloc unit ID
2882303763120062464 (type "In-row data"). Column "d" value is out of
range for data type "datetime". Update column to a legal value.
As suggested above ,these errors usually occurs when Precision and scale are not preserved .If your comfortable with SSIS then you can achieve to get those rows which are corrupt .Taking the values which Martin Smith created
CREATE TABLE T(ID int ,C DECIMAL(38,0));
INSERT INTO T VALUES(1,9999999999999999999999999999999999999)
The above table reproduces the error . Here the first column represents the primary key . I inserted around 1000 rows out of which few were corrupted values . Below is the SSIS package design
In the Data Conversion ,i took the column C which had errors and tried to cast it to Decimal(38,0) .Since a conversion or truncation error will occur ,therefore i redirected the error rows to an OLEDB command which basically updates the table and sets the column to NULL
Update T
Set C=NULL
where ID=?
The value of C and ID will be directed to oledb command .In case if there is no error then i'm just inserting into a table ( Actually no need to do this ).This will work if you have a primary key column in your table .
In case if there is any error in date time column a sql query can be written to verify the format of datetime values .Please go through the MSDN link for valid date time value
Select * from YourTable where ISDATE(Col)!=1
I think you can fetch data with cursor. please try again with cursor query such as below query :
DECLARE VerifyCursor CURSOR FOR
SELECT *
FROM MyTable
WHILE 1=1 BEGIN
BEGIN Try
FETCH FIRST FROM VerifyCursor INTO #Column1, #Column2, ...
INSERT INTO #MyTable2(Column1, Column2,...)
VALUES (#Column1, #Column2, ...)
END TRY
BEGIN CATCH
END CATCH
IF (##FETCH_STATUS<>0) BREAK
End
OPEN VerifyCursor
CLOSE VerifyCursor
DEALLOCATE VerifyCursor
Replacing the bad data is simple with an update:
UPDATE table SET column = NULL WHERE key_column = 'Some value'

Problem convert column values from VARCHAR(n) to DECIMAL

I have a SQL Server 2000 database with a column of type VARCHAR(255). All the data is either NULL, or numeric data with up to two points of precision (e.g. '11.85'). I tried to run the following T-SQL query but received the error 'Error converting data type varchar to numeric'
SELECT CAST([MyColumn] AS DECIMAL)
FROM [MyTable];
I tried a more specific cast, which also failed.
SELECT CAST([MyColumn] AS DECIMAL(6,2))
FROM [MyTable];
I also tried the following to see if any data is non-numeric, and the only values returned were NULL.
SELECT ISNUMERIC([MyColumn]), [MyColumn]
FROM [MyTable]
WHERE ISNUMERIC([MyColumn]) = 0;
I tried to convert to other data types, such as FLOAT and MONEY, but only MONEY was successful. So I tried the following:
SELECT CAST(CAST([MyColumn] AS MONEY) AS DECIMAL)
FROM [MyTable];
...which worked just fine. Any ideas why the original query failed? Will there be a problem if I first convert to MONEY and then to DECIMAL?
Thanks!
It's likely that this depends on whether the decimal symbol is a comma or a dot. Here are some test queries and their results:
select CAST('3.6' as decimal) -- 3.60
select CAST('3,6' as decimal) -- Error converting data type varchar to numeric.
select CAST('3.6' as money) -- 3.60
select CAST('3,6' as money) -- 36.00 (!)
select ISNUMERIC('3.6') -- 1
select ISNUMERIC('3,6') -- 1
The documentation for ISNUMERIC says :
ISNUMERIC returns 1 when the input
expression evaluates to a valid
integer, floating point number, money
or decimal type; otherwise it returns
0
Both 3.6 and 3,6 can be cast to money, so that's why isnumeric('3,6') returns 1.
To resolve this issue, replace the comma's with dots (or vice versa, if your system uses comma as the decimal symbol):
select cast(replace('3,6',',','.') as decimal)
Another option is to change the "decimal symbol" in Windows. It's in Config Panel -> Regional and Language -> Formats -> Additional Settings -> Numbers.
Another cause is empty strings. For example, if you use the import wizard to import a CSV file, empty fields will become empty strings, not nulls.
This is a common idiom for fixing this:
CAST(NULLIF([number_as_a_string],'') AS decimal(13,2))
Select CAST(isnull(MyColumn,0) as Decimal(4,2))
FROM [MyTable];

Resources