Recently I was Playing with SQL Server data types, and a Large number of data in the table and trying to figure out the Performance with Varchar and Numeric data. But, I got some error which I don't think should not have been but it is. My problem is below :
I have a table :
create table sa(f1 varchar(100))
I have a Stored Procedure that Inserts 100000 data into the table :
create proc sad
as
begin
declare #i int=0
while #i<100000
begin
insert into sa values(#i)
set #i=#i+1
end
end
exec sad
And I have tested the following:
select CONVERT(int,f1) from sa //Works Fine, i tested after the Problem
select sum(convert(int,f1)) from sa //Didn't Worked, So i tested above and Below
select sum(convert(decimal(18,2),f1)) from sa //And, it again works fine
But, When I sum Converting F1 to Int, it shows me an error.
But, when I only select Converting to Int it's fine.
And, when I sum Converting F1 to decimal it works Fine.
What is the SUM function data type?
On the Above data, it works well with Decimal but not Int?
Why?
I'm Getting the Following error
Arithmetic overflow error converting expression to data type int.
You're summing as INT which has a range that cannot hold that sum.
The DECIMAL can.
The sum of all values from 1 up to 99999 is 4999950000, the maximum INT value is 2147483647, less than half of what the sum ends up as.
When you sum INT's, you're getting a new INT. When you're summing DECIMAL's, you're getting a new DECIMAL, so the input type defines the output type.
You can switch to using bigint instead, and it should "be fine".
Also, on a second note, please don't store numbers as text!
According to MS documentation (see https://learn.microsoft.com/en-us/sql/t-sql/functions/sum-transact-sql), the SUM() function returns values of a different type, according to the datatype of the column you are adding: if the column is of type int, tinyint, or smallint, then SUM returns values of type int.
Converting to bigint or decimal makes SUM() return a larger datatype, this explains why in that case you have no overflow.
Expect you have exceeded the maximum int value that SQL Server allows (2,147,483,647) - see https://learn.microsoft.com/en-us/sql/t-sql/data-types/int-bigint-smallint-and-tinyint-transact-sql.
Decimal allows a far higher limit of up to 10^38 - 1 (i.e. 1 with 38 zeros after) - see https://learn.microsoft.com/en-us/sql/t-sql/data-types/decimal-and-numeric-transact-sql.
However, if the values are of type int, I wouldn't recommend converting to decimal. Decimal values are useful when you have figures with possible numbers after the decimal place with a known precision and scale (e.g. for currency, percentages etc.) As another poster has suggested, the best conversion here would be to a bigint:
select sum(cast(f1 as bigint)) from sa
Looks like your resulting sum is too big for int, use bigint, also check int, bigint, smallint, and tinyint (Transact-SQL)
select sum(convert(bigint,f1)) from sa
Try this:
CONVERT(NUMERIC(18,2),SUM(0))
Related
How come string can contain integer. Even if I assume string storing numeric values as string, but even i can use in it calculation and getting the result as well. Just to try I wrote 5 in inverted commas and still calculation works fine. Not sure how?
declare #x varchar(20)
declare #y int
select #x='5'
select #y=6
select #x+#y
SQL Server -- and all other databases -- convert values among types when the need arises.
In this case, you have + which can be either string concatenation or number addition. Because one argument is an integer, it is interpreted as addition, and SQL Server attempts to convert the string to a number.
If the string cannot be converted, then you will get an error.
I would advise you to do your best to avoid such implicit conversions. Use the correct type when defining values. If you need to store other types in a string, use cast()/convert() . . . or better yet, try_cast()/try_convert():
try_convert(int, #x) + #y
A varchar can contain any character from the collations codepage you are using. For the purposes of this answer, I'm going to assume you're using something like the collation SQL_Latin1_General_CP1_CI_AS (which doesn't have any "international" characters, like Kanji, Hiragana, etc).
You first declare the variable #x as a varchar(20) and put the varchar value '5' in it. This is not an int, it's a varchar. This is an important distinction as a varchar and a numerical data type (like an int) behave very differently. For example '10' has a lower value than '2', where as the opposite is true for 10 and 2. (This is one reason why using the correct data type is always important.)
Then the second variable you have is #y, which is an int and has the value 6.
Then you have your expression SELECT #x+#y;. This has 2 parts to it. Firstly, as you have 2 datatypes, Data Type Precedence comes into play. int has a higher precedence than a varchar, and so #x is implicitly converted to an int. Then the expression is calculated, uses + as an addition operator (not a concatenation operator). Therefore the expression is effectively derived like this:
#x + #y = '5' + 6 = CONVERT(int,'5') + 6 = 5 + 6 = 11
SQL Server uses the following precedence order for data types:
user-defined data types (highest)
sql_variant
xml
datetimeoffset
datetime2
datetime
smalldatetime
date
time
float
real
decimal
money
smallmoney
bigint
int
smallint
tinyint
bit
ntext
text
image
timestamp
uniqueidentifier
nvarchar (including nvarchar(max) )
nchar
varchar (including varchar(max) )
char
varbinary (including varbinary(max) )
binary (lowest)
Just need your help here.
I have a table T
A (nvarchar) B()
--------------------------
'abcd'
'xyzxcz'
B should output length of entries in A for which I did
UPDATE T
SET B = LEN(A) -- I know LEN function returns int
But when I checked out the datatype of B using sp_help T, it showed column B as nvarchar.
What's going on ?
select A
from T
where B > 100
also returned correct output?
Why is nvarchar working with logical operators ?
Please help.
Check https://learn.microsoft.com/en-us/sql/t-sql/data-types/data-type-conversion-database-engine?view=sql-server-2017 where it is said that data types are converted explicitly or implicitly when you move, compare or store a variable. In your case, you are comparing column B with 100, forcing sql server to implicitly convert it to integer type (check the picture about conversions on the same page). As a prove, try to alter a row putting some text in column B and, after repeating your select query B>100, sql server will throw a conversione error trying to obtain an integer out of your text.
It works because of implicit conversion between types.
Data type precedence
When an operator combines expressions of different data types, the data type with the lower precedence is first converted to the data type with the higher precedence. If the conversion isn't a supported implicit conversion, an error is returned.
Types precedence:
16. int
...
25. nvarchar (including nvarchar(max) )
In you example:
select A
from T
where B > 100
--nvarchar and int (B is implicitly casted to INT)
when adding a column to a table in ssms, not adding a datatype a "default" datatype is chosen. for me on 2017 developer it's nchar(10). if you want it to be int define the column with datatype of int. in tsql it'd be
create table T (
A nvarchar --for me the nvarchar without a size gives an nvarchar(2)
,B int
);
sp_help T
--to make a specific size, largest for nvarchar is 4000 or max...max is the replacement for ntext of old, as.
create table Tmax (
A nvarchar(max)
,B int
);
--understanding nvarchar and varchar for len() and datalength()
select
datalength(N'wibble') datalength_nvarchar -- nvarchar is unicode and uses 2 bytes per char, so 12
,datalength('wibble') datalength_varchar -- varchar uses 1 byte per so 6
,len(N'wibble') len_nvarchar -- count of chars, so 6
,len('wibble') len_varchar -- count of char so still 6
nvarchar(max) and varchar(max)
hope this helps, the question is a bit discombobulated
I have a table where a column has data such as ($23,324.09). If it's $23,324.09, then I can convert it to money, then a float. But the ( ) is causing errors when I try that. Will I have to apply a UDF to that column or is there another way to convert such a value.
NOTE: I don't have control on how the data gets to that table.
You could also use PARSE or TRY_PARSE
SELECT TRY_PARSE('($23,324.09)' AS MONEY USING 'en-US')
Returns
-23324.09
using a few replace():
declare #col varchar(32) = '($23,324.09)'
select convert(decimal(19,4),replace(replace(replace(replace(#col,'(','-'),',',''),')',''),'$',''))
returns: -23324.0900
Running a simple test, this is runs in about 1/3rd of the time as the parse() equivalent.
dbfiddle: http://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=675f6eb70986bd24880c66a8be4f5cbd
Here is a shorter alternative that uses the money data type:
select convert(money,replace(replace(#col,'(','-'),')',''))
dbfiddle: http://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=31bfc3e1e48b8f863e89bcef9a6ffddc
I have a database table of that I have used to store the data returned from a web spider. I have a column that contains ticket prices for different events all in the varchar type (as the scrapy spider has to scrape the data in unicode). I'm trying to return the min price of the column and since the min() function only works for data of type INT, I tried to convert the column to integers using a solution from this SO post:
ALTER TABLE vs_tickets ALTER COLUMN ticketprice TYPE integer USING (ticketprice::integer);
but I got the error: ERROR: invalid input syntax for integer:
I also tried: change_column :vs_tickets, :ticketprice, 'integer USING CAST(ticketprice AS integer)' but that didn't work either.
What is the proper way to convert the column to type INT?
Edit:
You have decimal places in the string, so a simple cast is not going to work. You can do a double conversion:
cast(cast(ticketprice as decimal(10, 2)) as int)
or:
(ticketprice::decimal(10, 2))::int
(The parens are not strictly necessary.)
EDIT:
Or, as Erwin points out, just use numeric:
(ticketprice::numeric)::int
Postgres is much smarter about numeric than most other databases . . . after all, it supports numbers that are egregiously large ;)
The final query is:
ALTER TABLE vs_tickets
ALTER COLUMN ticketprice TYPE integer USING (ticketprice::numeric::integer);
I'm going to bet on your column have wrong characters.
Also you may want use float or numeric because you will lose decimals if convert to integers.
You need create a function to check if a text is numeric like this isnumeric-with-postgresql
Then check each row like this
select ticketprice
from vs_tickets
where ISNUMERIC(ticketprice) = false;
As your comment you also should try
SELECT ticketprice::float
You will be best off adding an INT column, moving your data with a cast and then removing the old varchar column.
ALTER TABLE vs_tickets ADD COLUMN ticketprice_int TYPE int;
GO
update vs_tickets SET ticketprice_int = cast(ticketprice as int);
// if you fail to cast the varchar to int you can use Gordon's method
// update vs_tickets SET ticketprice_int = cast(cast(ticketprice as decimal(10, 2)) as int);
GO
ALTER TABLE vs_tickets DROP COLUMN ticketprice;
GO
ALTER TABLE vs_tickets RENAME COLUMN ticketprice_int to ticketprice;
GO
With this at minimum you will be able to tell if and where a cast/convert fails and be able to check and recheck at each step before you can't turn back.
I don't have that much experience with writing SQL queries and I have hit upon a problem. I have read in a table of data into a temporary table (#Temp_Results) and need to change the format of various columns before moving the data to the end table.
What Im trying to do below is take a column (Oil2) that is an nvarchar and convert it to a tinyint and put the result into a new column (Oil4) then drop Oil2 - I realise I will loose decimal places but thats not a problem. The CASE statement is designed to capture anything that is not a number, seeing as the original datatype is nvarchar there could be anything in there and I'm only interested in the numbers.
However when I run the code I get 'Error converting data type nvarchar to float' pointing towards the 'UPDATE' line of code and I cant figure out how to get round it.
Can any of you guys spot my rookie mistake?
ALTER TABLE tempdb..#Temp_Results /*Add new column with datatype of tinyint*/
ADD Oil4 tinyint
GO
UPDATE tempdb..#Temp_Results
SET tempdb..#Temp_Results.Oil4 = CASE
WHEN ISNUMERIC(tempdb..#Temp_Results.Oil2)=1
THEN CAST(ROUND(CAST(tempdb..#Temp_H_Results.Oil2 as float), 0) AS tinyint)
ELSE NULL
END
ALTER TABLE tempdb..#Temp_H_Results /*Drop redundant column of data in wrong (nvarchar) format*/
DROP COLUMN Oil2
Go
Try casting to Decimal in stead of float
CONVERT(varchar(28), cast(tempdb..#Temp_H_Results.Oil2 as decimal(28,0)))