How can I use cast in SQL with multiple column selections? - sql

I tried this to get our revenue:
SELECT
CAST(stays_in_week_nights AS int) +
CAST(stays_in_weekend_nights AS int) *
CAST(adr AS int) AS revenue
FROM
Hotels
I expected to get a number as the result of the calculation - but instead, I get this error:
Msg 245, Level 16, State 1, Line 2
Conversion failed when converting the varchar value '111.6' to data type int.

cast is as a decimal instead of as an int. Assuming it is the adr field that is the problem, it would be
SELECT
(CAST(stays_in_week_nights AS int) + CAST(stays_in_weekend_nights AS int))
* CAST(adr AS decimal(10,2)) AS revenue
FROM Hotels
What you would need for the two parameters in decimal(10,2) would depend on your data. Decimal(10,2) would give you 8 places before the decimal point and 2 after.
Also, you may want to add an extra set of parentheses to make sure you are calculating it correctly. Do you want (week+weekend) * adr or week + (weekend*adr)
What you are currently going to get is the second. Even if that is what you want, adding parentheses would clarify to the next developer that that is what you intended to do

Related

How can I use Sum for a string function?

An Employee has multiple contract hours, I want to sum the total hours of the contract for each employee, but the data within the column of total contract hours is in this format: 35.00 - Contract hours
When trying to run the below script:
SELECT DISTINCT
EmpId
,Substring(Contract_Hours,1,5) AS TotalContractHours
,SUM(CAST(Substring(Contract_Hours,1,5) AS INT))
--OR ,SUM(CAST(Substring(Contract_Hours,1,5) AS decimal(1,1)))
FROM tbl.Employee
GROUP BY
EmpID
,Substring(Contract_Hours,1,5)
I am getting error:
Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value '28.00' to data
type int.
I tried to run it without the sum aggregate and it worked, so i think its something to do with the sum and data type that is not converting
what am I doing wrong? Please help
Since the hours is in format xx.yy you have 2 options:
1. Take only the integer part and cast as int:
SELECT EmpId
,Substring(Contract_Hours,1,2) AS TotalContractHours
,SUM(CAST(Substring(Contract_Hours,1,2) AS INT))
FROM tbl.Employee
GROUP BY
EmpID
,Substring(Contract_Hours,1,2)
2. Take whole part and cast as decimal:
SELECT EmpId
,Substring(Contract_Hours,1,5) AS TotalContractHours
,SUM(CAST(Substring(Contract_Hours,1,5) AS DECIMAL(18,2)))
FROM tbl.Employee
GROUP BY
EmpID
,Substring(Contract_Hours,1,5)
The Error is in the Substring. A Substring(1,2) will work
But, a (1,5) will not. Most likely due to the Decimal. You might have to Double Convert/Cast to Decimal, then, back to Integer
Like this:
SELECT DISTINCT
EmpId
,Substring(Contract_Hours,1,5) AS TotalContractHours
,SUM(Convert(int,CAST(Substring(Contract_Hours,1,5) AS decimal(18,2))))
--OR ,SUM(CAST(Substring(Contract_Hours,1,5) AS decimal(1,1)))
FROM tbl.Employee
GROUP BY
EmpID
,Substring(Contract_Hours,1,5)
If you truly are stuck with this terrible design, then I would attempt to wrap up your query to make it as safe as possible, e.g.:
SELECT
EmpId,
LEFT(Contract_Hours, 5) AS TotalContractHours,
SUM(CONVERT(INT, CASE WHEN ISNUMERIC(LEFT(Contract_Hours, 5)) = 1 THEN CONVERT(NUMERIC(19,2), LEFT(Contract_Hours, 5)) ELSE 0 END)) AS SummedHours
FROM
tbl.Employee
GROUP BY
EmpID,
LEFT(Contract_Hours, 5);
How does this work?
We don't need to bother with SUBSTRING as you always start at the leftmost position of the string,so LEFT is simpler. ISNUMERIC will tell us whether we end up with a number or not, to save us getting an exception by attempting to CONVERT something from a string to a number. So if the leftmost 5 characters are numeric, we CONVERT them to a 2dp decimal, then to an INT, then SUM them.
You could try turning the value into numerical value first. Subsequently convert it in decimal. With the following code it should work.
SUM(CAST(CAST(Contract_Hours AS decimal(10,2)) AS int))

Msg 242: conversion of a varchar data type to a datetime data type resulted in an out-of-range value

I have gone through a bunch of posts here with this error and tried changing data types and used Convert but nothing seems to fix this. So I would like to ask for some help here. I will try to give as much info, but feel free to ask if its not enough.
This is where I am getting the error:
Insert into prompt(ID, Date)
select
ROW_NUMBER() over (order by b.IDLoc),
[dbo].[fn_GetGPtime](cast (replace(DateCollected, '/', '-') + ' ' + a.UTCTime as datetime))
from
Img a
inner join
Tloc b on a.Filename = b.filename
order by
b.IDLoc
The Date column in prompt table has a datatype of float. UTCTime and DateCollected are both varchar(20)
The error is:
Msg 242, Level 16, State 3, Line 274
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
Here is the function:
[dbo].[fn_GetGPtime] (#UTCtime datetime)
returns varchar(50)
AS
BEGIN
return (DATEPART (WEEKDAY, #UTCtime) - 1) * 86400 ---day
+ DATEPART (HOUR, #UTCtime) * 3600 ---hour
+ DATEPART (MINUTE, #UTCtime) * 60 ----minutes
+ DATEPART (SECOND, #UTCtime) ---second
+ (DATEPART (MILLISECOND, #UTCtime)) * 0.001 ---ms
+ (DATEPART (MICROSECOND, #UTCtime)) * 0.000001 ---us
+ 16 ----leap seconds
end;
To get an idea of the data itself:
How do I fix this issue?
Your error message could mean two different things: that you have non-convertible data in some cells, or that field's data are not convertible to datetime at all.
You can use try_convert instead of convert to figure out which it is. It will solve your problem if you have a few completely unusable values (i.e. bad data); you'll get nulls for bad data and good conversion for good data. If the overall conversion is never going to work you'll get all nulls and you'll know it isn't just a few bad values.
Another thing you could try is converting from float to numeric before converting to datetime. I find that float formatted data are awful for conversions and converting to numeric can remove many issues. You'd have something like convert(datetime, convert(numeric(18,2), UTCTime))
Use convert instead of cast. When using convert, you can specify the format of the string representing the date.
Once you've converted DateCollected to datetime, you can cast a.UTCTime to datetime and add them together:
Insert into prompt(ID,Date)
select ROW_NUMBER() over (order by b.IDLoc),
[dbo].[fn_GetGPtime](convert(datetime, DateCollected, 101) + cast(a.UTCTime as datetime))
from Img a inner join Tloc b on a.Filename=b.filename
order by b.IDLoc
(assuming a.UTCTime is either varchar or time)
What worked for me, solving this error on an input line such as
SELECT CAST(N'2003-12-01 14:20:47.000' AS DateTime) AS result
Msg 242 Level 16 ...
is the magic instruction:
SET DATEFORMAT ymd;

Remove decimal using SQL query

I want to convert my decimal SQL query result in percent. Example I have a 0.295333 I want it to be 30% and if I have a 0.090036 I want it to be 9%.
This is what I have so far.
(100 * (sample1/ sample2) ) as 'Percent'
I also tried this one but the problem is result comes with ".00" and I don't know how to remove the decimal.
cast (ROUND(100 * (sample1 / sample2),0) As int ) as 'Percent'
Try with the below script..
cast (100 * Round((sample1 / sample2),2) As int ) as 'Percent'
So as some of the comments pointed out you may need to pay attention to your datatype if one or both of the original columns that you get your decimal from are integer.
One easy way of dealing with that is something like this:
ColA * ColB * 1.0 which will make sure that your integers are treated as decimals
So if you have SQL Server 2012+ you can use Format and not mess with rounding at all. Like this FORMAT(YourDecimal,'#%'), yep that simple.
;WITH cteValues AS (
SELECT 0.295333 as OriginalValue
UNION ALL
SELECT 0.090036 as OriginalValue
)
SELECT
OriginalValue
,FORMAT(OriginalValue,'#%') as PercentFormat
FROm
cteValues
If you are pre 2012 and do not have format an easy way is to round to the 100th then times by 100 and cast as int CAST(ROUND(YourDecimal,2) * 100 AS INT)
;WITH cteValues AS (
SELECT 0.295333 as OriginalValue
UNION ALL
SELECT 0.090036 as OriginalValue
)
SELECT
OriginalValue
,CAST(ROUND(OriginalValue,2) * 100 AS INT) as PercentInt
FROm
cteValues
Because an INT cannot by definition have decimal places, if you are receiving .00 with the method similar to this or the one you have tried, I would ask the following.
Are you combining (multiplying etc.) the value after casting with another column or value that may be decimal, numeric, or float?
Are you looking at the query results in a program outside of SSMS that could be formatting the results automatically, e.g. Excel, Access?
Address your assumptions first.
How does ROUND work? Does it guarantee return values and if so, how? What is the precedence of the two columns? Does Arithmetic operators influence the results and how?
I only know what I do not know, and any doubt is worth an investigation.
THE DIVIDEND OPERATOR
Since ROUND always returns the higher precedence, this is not the problem. It is in fact the divide operator ( / ) that may be transforming your values to an integer.
Always verify the variables are consistently of one datatype or CAST if either unsure or unable to guarantee (such as insufficiently formatted. I.e. DECIMAL(4,2) instead of required DECIMAL(5,3) ).
DECLARE #Sample1 INT
, #Sample2 DECIMAL(4,2);
SET #Sample1 = 50;
SET #Sample2 =83.11;
SELECT ROUND( 100 * #Sample1 / #Sample2 , 0 )
Returns properly 60.
SELECT ROUND( 100 * #Sample2 / #Sample1 , 0)
Incorrectly turns variables into integers before rounding.
The reason is that DIVIDE - MSDN in SQL may return the higher precedence, but any dividend that is an integer returns another integer.
UPDATE
This also explains why the decimal remains after ROUND...it is of higher precedence. You can add another cast to transform the non-INT datatype to the preferred format.
SELECT CAST( ROUND( <expression>, <Length>) AS INT)
Note that in answering your question I learned something myself! :)
Hope this helps.

SQL Server 2008: Varchar Conversion to Numeric Data Overflow, Probably Because Some Are Ranges

I'm working on a query with a varchar column called ALCOHOL_OZ_PER_WK. Part of the query includes:
where e.ALCOHOL_OZ_PER_WK >= 14
and get the errors:
Arithmetic overflow error converting varchar to data type numeric.
and:
Error converting data type varchar to numeric.
Looking into the values actually stored in the column, the largest look close to 100, but some of the entries are ranges:
9 - 12
1.5 - 2.5
I'd like to get the upper limit (or maybe the midpoint of the range) from rows with entries like this and have it be the value being compared to 14.
What would be the (or an) easy way to do this?
As always, thank you!
Your DB is obviously result of some survey, and it seems to contain the original survey data. The usual way is to run this through an ECCD (Extract, Clean, Conform, Deliver) process and store clean and standardized data into a separate database (maybe a warehouse) which can then be used for analytics and reporting.
If you have SSIS use data profiling task to get an idea of types of strings you have in there. The Column Pattern Profile reports a set of regular expressions on the string column, so you will get an idea of what's inside those strings. If you do not have SSIS, you can use eobjects DataCleaner to do the same.
If you can not spare a new database or at least a new table -- at minimum add a numeric column to this table and then extract numeric values form those strings into the new column. You may want to use "something else" (SSIS, Pentaho Kettle, Python, VB, C#) to do this -- in general T-SQL in not very good at string processing.
My guess is that this is not the only column that has garbage inside, so any analysis that you may run on this may be worthless.
And if you still think that the ranges are the only problem, this example may help:
First some data
DECLARE #myTable TABLE (
AlUnits varchar(10)
) ;
INSERT INTO #myTable
(AlUnits )
VALUES ( '10' )
, ( '15' )
, ( '20' )
, ( '7 - 12' )
, ( '3 - 5' )
;
The query splits records into two groups, numeric and not numeric -- assumed ranges.
;
WITH is_num
AS ( SELECT CAST(AlUnits AS decimal(6, 2)) AS Units_LO
,CAST(AlUnits AS decimal(6, 2)) AS Units_HI
FROM #myTable
WHERE ISNUMERIC(AlUnits) = 1
),
is_not_num
AS ( SELECT CAST( RTRIM(LTRIM(LEFT(AlUnits,
CHARINDEX('-', AlUnits) - 1)))
AS decimal(6,2)) AS Units_LO
,CAST(RTRIM(LTRIM(RIGHT(AlUnits,
LEN(AlUnits)
- CHARINDEX('-', AlUnits))))
AS decimal(6,2)) AS Units_HI
FROM #myTable
WHERE ISNUMERIC(AlUnits) = 0
)
SELECT Units_LO
,Units_HI
,CAST(( Units_LO + Units_HI ) / 2.0 AS decimal(6, 2)) AS Units_Avg
FROM is_num
UNION ALL
SELECT Units_LO
,Units_HI
,CAST(( Units_LO + Units_HI ) / 2.0 AS decimal(6, 2)) AS Units_Avg
FROM is_not_num ;
Returns:
Units_LO Units_HI Units_Avg
----------- ----------- ----------
10.00 10.00 10.00
15.00 15.00 15.00
20.00 20.00 20.00
7.00 12.00 9.50
3.00 5.00 4.00
Not sure about easy ways.
A proper way is to store the numbers in two columns, ALCOHOL_OZ_PER_WK_MIN and ALCOHOL_OZ_PER_WK_MAX.
As you say you need to calculate numeric values, which you can then use in your query.
Probably the easiest way is to use some simple logic to calculate the average or upper limit using string functions, and string to numeric functions.
If all you want is the upper limit, just get the characters after the '-' and use that.
"probably because some are ranges" - do you get that "range" is not a SQL Server Data type? You've got non-numeric data you're trying to convert into numeric data, and you've got a scalar value you're comparing to a non-scalar value.
This database has some issues.

SQL IsNumeric Returns True but SQL Reports 'Conversion Failed'

Assuming the following data:
Column1 (data type: varchar(50))
--------
11.6
-1
1,000
10"
Non-Numeric String
I have a query, which is pulling data from this column and would like to determine if the value is a number, then return it as such in my query. So I am doing the following
SELECT CASE
WHEN IsNumeric(Replace(Column1, '"', '')) = 1 THEN Replace(Column1, '"', '')
ELSE 0
END AS NumericValue
SQL is reporting back:
Conversion failed when converting the varchar value '11.6' to data type int.
Why? I have also tried to force cast this:
SELECT CASE
WHEN IsNumeric(Replace(Column1, '"', '')) = 1 THEN cast(Replace(Column1, '"', '') AS float)
ELSE 0
END AS NumericValue
And I got:
Error converting data type varchar to float.
You need to replace comma with a period:
CAST(REPLACE(column, ',', '.') AS FLOAT)
SQL Server outputs decimal separator defined with locale, but does not unterstand anything but a period in CASTs to numeric types.
First convert the string to money, then covert it to any other numeric format since money type gives a true numeric string always. You will never see an error then.
Try the following in your query, and you'll know what I am talking about. Both will return 2345.5656. The Money datatype is rounded to 4 decimal places, and hence the casting causes rounding to 4 decimal places.
SELECT CAST('2,345.56556' as money), CAST('$2,345.56556' as money)
Cast( cast('2,344' as money) as float) will work perfectly or
cast( cast('2,344' as money) as decimal(7,2)) will also work.
Even cast(CAST('$2,345.56556' as money) as int ) will work perfectly rounding it to nearest integer.
There are many issues with SQL isnumeric. For example:
select isnumeric('1e5')
This will return 1 but in many languages if you try to convert it to a number it will fail. A better approach is to create your own user defined function with the parameters you need to check for:
http://www.tek-tips.com/faqs.cfm?fid=6423
ISNUMERIC returns 1 when the input expression evaluates to a valid integer, floating point number, money or decimal type;
So the problem is it is a valid number but not a valid int.
Kyle,
I think this solves the problem. The problem lies in the fact that the ELSE clause initializes your result to be an INTEGER. By making an explicit typecast to FLOAT and adding the suggestion of Quassnoi, it seems to work.
DECLARE #MyTable TABLE (Column1 VARCHAR(50))
INSERT INTO #MyTable VALUES('11.6')
INSERT INTO #MyTable VALUES('-1')
INSERT INTO #MyTable VALUES('1,000')
INSERT INTO #MyTable VALUES('10" ')
INSERT INTO #MyTable VALUES('Non-Numeric String')
SELECT CASE WHEN ISNUMERIC(REPLACE(Column1,'"','')) = 1 THEN REPLACE(REPLACE(Column1,'"',''), ',', '.') ELSE CAST(0 AS FLOAT) END
FROM #MyTable
Regards,
Lieven
IsNumeric(' ') also returns 1, but then CAST as int blows up. Brendan above says write your own function. He is correct.
This solution does not work in all cases (specifically numbers with money and/or thousand separators). Concatenate an exponent representation to the end of the number which is represented by a string...ISNUMERIC() works fine from there. Examples below:
-- CURRENT ISNUMERIC RESULTS
SELECT ISNUMERIC('11.6'); --1
SELECT ISNUMERIC ('-1'); --1
SELECT ISNUMERIC('1,000'); --1
SELECT ISNUMERIC('10"'); --0
SELECT ISNUMERIC('$10'); --1
-- NEW ISNUMERIC RESULTS
SELECT ISNUMERIC('11.6'+'e+00'); --1
SELECT ISNUMERIC ('-1'+'e+00'); --1
SELECT ISNUMERIC('1,000'+'e+00'); --0
SELECT ISNUMERIC('10"'+'e+00'); --0
SELECT ISNUMERIC('$10'+'e+00'); --0
This, at the very least, standardizes the format for using the REPLACE() function.
I have just meet this issue.
You can try this solution if you don't mind about limitation of decimal length.
CONVERT(numeric, CONVERT(money, '.'))
NOTE:
It is supported in SQL Server 2008 or above.
Money range is : -922,337,203,685,477.5808 to 922,337,203,685,477.5807 - four decimals.