I have a table that has non-numeric values in the source.
I am using a CTE to filter down to a set of values that is all numeric.
However, when I try to convert the filtered list to float, I get an error.
Goal is to find all Lab Results that are less than 8.0
CTE:
;with labs AS (
SELECT LabResult_Result
FROM VIEW_PatientLabResult
WHERE (LabResult_DateTimeOfObservation between '1/1/2012' and '1/1/2013') and
MasterLabCode_Description='Hemoglobin (Hgb) A1c'
)
Results in ~250 rows, all of which are decimal (manually checked).
Cannot get to convert them to float for comparison. Best I have so far is:
select * from
(
select *, CAST( labresult_result as float) as Converted
FROM labs
) Conv
WHERE Conv.Converted < 8.0
This results in Error converting data type varchar to float.
Without the WHERE clause, the query runs fine.
I am thinking that SQL is trying to convert the entire results before the CTE filter. Is there a way to make it run in order?
SQL only guarantees the order of execution when you use a case statement. (Although as Aaron points out, this is not true for aggregated values in a group by expression.)
Try this instead:
select *
from (select *, (case when ISNUMERIC(labresult_result) = 1 then CAST(labresult_result as float)
end) as Converted
FROM labs
) conv
where conv.converted < 8.0
In the original query, SQL is free to rearrange the where clauses to optimize the query -- even between CTEs and subqueries. This can result in an unexpected error. Remember, SQL is a descriptive language that describes the output, not a procedural language that specifies how things are processed.
Related
I'm learning SQL on codecademy
There's an example of nested aggregate function:
SELECT ROUND(AVG(price), 2)
FROM fake_apps;
ROUND() requires a number as first parameter, how can AVG(price) be plugged in here? What's the datatype of it?
If I change the code to:
SELECT ROUND(SELECT AVG(price)
FROM fake_apps, 2)
FROM fake_apps;
The code throws a syntax error.
The below code gives a syntax error because ROUND is a scalar function. It expects exactly one value as the first parameter.
SELECT ROUND(SELECT AVG(price)
FROM fake_apps, 2)
FROM fake_apps;
SELECT AVG(price) FROM fake_apps returns a set of rows.
On the other hand, AVG is an aggregate function. It operates on a set of rows. The return type is numeric.
I don't think it needs nested select query, at least as per your requirement example you have provided. This may be resolved with following query also.
SELECT ROUND( AVG (price), 2 ) FROM fake_apps
DataType should be decimal or numeric with precision (for e.g. numeric (18,3) )
Let me know if I interpreted as wrong question and correct me, so I can try another way.
I have a query from core of data which is nvarchar and all values are '00:00:00' format. I want to convert it into long. When I try to convert top 1000 it working fine but problem with all values. Query show in below
SELECT DATEDIFF(second, '00:00', CAST(TimeSpent AS time(7)))* cast(1000 as bigint) + RIGHT(CAST(TimeSpent AS time(7)),7) FROM [mtr].[MatterDocument]
The error statement is
Conversion failed when converting date and/or time from character string
How can I find which value failed to convert?
I suggest that there is some bad data in your MatterDocument table. SQL Server does not support regex searches, but fortunately its LIKE operator does support some primitive regex which we can use:
SELECT *
FROM [mtr].[MatterDocument]
WHERE TimeSpent NOT LIKE '[01][0-9]:[0-5][0-9]:[0-5][0-9]' AND
TimeSpent NOT LIKE '2[0-3]:[0-5][0-9]:[0-5][0-9]';
Demo
You may verify in the demo that bad, non acceptable, time strings are being flushed out. The above query should also work to flush out strings which maybe aren't even time values at all, and somehow made it into your table.
The best long term fix would be to correct your data at its source, and then bring the data into SQL Server as a bona fide date/time type.
Edit: TRY_CAST, as described by #Denis in his answer, might be another approach. But this would require SQL Server 2012 or later. The above query should still work in earlier versions.
Try to use TRY_CAST function to find the wrong rows (it returns NULL if it cannot convert the value)
SELECT c.TimeSpent, /*Any columns to identify rows */
FROM (
SELECT TimeSpent, /*Any columns to identify rows */
DATEDIFF(second, '00:00', TRY_CAST(TimeSpent AS time(7)))* cast(1000 as bigint)
+ RIGHT(TRY_CAST(TimeSpent AS time(7)),7) AS Converted
FROM [mtr].[MatterDocument]
) c
WHERE Converted IS NULL
You should find the bad values:
select timespent
from t
where try_cast(TimeSpent AS time(7)) is null;
This will enable you to find the bad values. They are probably times that exceed 23.
I would suggest doing the conversion more simply:
select (left(TimeSpent, 2) * 60 * 60 +
substring(TimeStpent, 4, 2) * 60 +
right(TimeSpent, 2)
) as seconds
This will do the conversion without the limitations of the SQL Server time data type.
I'm a B-grade SQL user, so bear with me. I have a field that is in the NVARCHAR format ("Year"), but all but only about 1 in 1000 records is something other than a number. Yes, this is a ridiculous way to do this, but we receive this database from a customer, and we can't change it.
I want to pull records from the database where the year field is greater than something (say, 2006 or later). I can ignore any record whose year doesn't evaluate to an actual year. We are using SQL server 2014.
I have created an embedded query to convert the data to a "float" field, but for whatever reason, I can't add a where clause with this new floating-point field. I originally tried using a "case-if" but I got the same result.
I'm pulling my hair out, as I'm either missing something really silly, or there's a bug in SQL server. When I look at the field in the little hint, it's showing as a float. When I run this, I get "Error converting data type nvarchar to float."
SELECT VL.Field_A,
VL.FLYear,
VL.Field_B
FROM
(select
Field_A,
cast ([Year] as float) as FLYear,
/* didn't work either*/
/*Convert(float, [Year]) as FLYear, */
Field_B
from CustomerProvidedDatabaseTable
where (Field_A like 'E-%' OR
Field_A like 'F-%')
and
(isnumeric(year)=1)
and
year is not null
) VL
/* this statement is the one it chokes on */
where
VL.FLYear >= 2006.0
If I remove the last "where" clause, it works fine, and the field looks like a number. If I change the last where clause to:
where VL.FLYear like '%2006%'
SQL Server accepts it, though of course it doesn't return me all the records I want.
Try to simplify it and just use TRY_CONVERT(DATETIME, aYearvalue) or TRY_PARSE which will return NULL for values it can't convert and continue to process valid rows. I think you can do away with the where clause as join and just work directly against the column like: (substitute the literal string after datetime with your column)
SET DATEFORMAT mdy;
Select YEAR(try_convert(datetime, '08/01/2017')) as value1
WHERE value1 >=2016;
Try cast/convert to a numeric data type. I have modified the last line of your query to do just that. Take a peek.
SELECT
VL.Field_A,
VL.FLYear,
VL.Field_B
FROM
(select
Field_A,
cast ([Year] as float) as FLYear,
/* didn't work either*/
/*Convert(float, [Year]) as FLYear, */
Field_B
from CustomerProvidedDatabaseTable
where (Field_A like 'E-%' OR
Field_A like 'F-%')
and
(isnumeric(year)=1)
and
year is not null
) VL
/* this statement is the one it chokes on */
where
ISNUMERIC(VL.FLYear) = 1
and
CAST(VL.FLYear AS INT) >= 2006
Check out the following link for cast and convert documentation:
https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql
NOTE: ISNUMERIC will return true ( a false positive for a value which has a scientific numerical value, e.g. 1E10, though I don't see this happening from your data).
Another option is TRY_CONVERT.
Documentation on TRY_CONVERT: https://learn.microsoft.com/en-us/sql/t-sql/functions/try-convert-transact-sql
Try using Cast . Use the below link to check in more detail about casting.
https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql
I have a query in which I want to select data from a column where the data is a date. The problem is that the data is a mix of text and dates.
This bit of SQL only returns the longest text field:
SELECT MAX(field_value)
Where the date does occur, it is always in the format xx/xx/xxxx
I'm trying to select the most recent date.
I'm using MS SQL.
Can anyone help?
Try this using ISDATE and CONVERT:
SELECT MAX(CONVERT(DateTime, MaybeDate))
FROM (
SELECT MaybeDate
FROM MyTable
WHERE ISDATE(MaybeDate) = 1) T
You could also use MAX(CAST(MaybeDate AS DateTime)). I got in the (maybe bad?) habit of using CONVERT years ago and have stuck with it.
To do this without a conversion error:
select max(case when isdate(col) = 1 then cast(col as date) end) -- or use convert()
from . . .
The SQL statement does not specify the order of operations. So, even including a where clause in a subquery will not guarantee that only dates get converted. In fact, the SQL Server optimizer is "smart" enough to do the conversion when the data is brought in and then do the filtering afterwards.
The only operation that guarantees sequencing of operations is the case statement, and there are even exceptions to that.
Another solution would be using PATINDEX in WHERE clause.
SELECT PATINDEX('[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]', field_value)
Problem with this approach is you really are not sure if something is date (e.g. 99/99/9999 is not date).
And problem with IS_DATE is it depends on configuration (e.g. DATEFORMAT).
So, use an appropriate option.
I am using sql server 2000 and facing round function issue like the following statement working fine.
SELECT ROUND(5 * 7.83, 1)
The result will be 39.2
But when I get these values from the table, it gives 39.1, meaning it truncates and does not round up.
SELECT ROUND(rate * qty, 1)
FROM tbl
The result will be 39.1
rate and qty columns data types are float. Insert 5 in qty and 7.83 in rate, then check it. How I can fix it?
Convert the table values to real,
SELECT ROUND(convert(real,rate)*convert(real,qty),1)
Your sample simply query is not reflective of the data types involved.
Try these two instead:
SELECT ROUND(5 * 7.83, 1)
SELECT ROUND(cast(5 as float) * cast(7.83 as float), 1)
The 2nd one matches your table data types. Float datatypes are not meant for precise decimal calculations, use a decimal type for those instead.
What Every Computer Scientist Should Know About Floating-Point Arithmetic
Without losing too much precision for normal numbers, you can just cast to decimal on the fly to force human-comprehensible decimal arithmetics, e.g.
SELECT ROUND(cast(rate as decimal(10,5)) * cast(qty as decimal(10,5), 1)
FROM tbl