If I have a cast in a where clause before I check another field first will it fail? - sql

I have a scenario where I am aggregating data between two systems. Unfortunately I am using a table adapter in Visual Studio.
Select * from prsnpsht where Cast(snp_check as bigint) > 10 and sourcereference Is Null
Union ALL
Select * from prsnpsht where sourcereference is not null
In the case above, the snp_check from the first system will be all numeric which will cast with the snp_check to bigint correctly and we know the data is from the first system because the sourcereference is null. The second system can have other characters in the snp_check field and when it tried to cast to bigint, it fails.
If I keep the structure of the query the way it is, will it attempt to cast all of the checks to big int before it analyzes the sourcereference condition, or is SQL Server smart enough to know that it should limit the data before it tries to cast the snp_check field?
I apologize in advance if this is a duplicate question, but I was unable to find it if it was answered before.

IF the SQL Server Version is 2012 or above try the following query
Select * from prsnpsht where try_convert(bigint,snp_check) > 10 and sourcereference Is Null
Union ALL
Select * from prsnpsht where sourcereference is not null
Try_Convert() returns a null value when snp_check is an invalid Bigint data

I speculate that for this query:
Select *
from prsnpsht
where Cast(snp_check as bigint) > 10 and sourcereference Is Null;
You are getting a conversion error.
In SQL Server 2012+, the solution is to use try_convert():
Select *
from prsnpsht
where try_convert(bigint, snp_check) > 10 and sourcereference Is Null;
In earlier versions, use a case:
Select *
from prsnpsht
where (case when snp_check not like '%[^0-9]%'
then convert(bigint, snp_check)
end) > 10 and
sourcereference Is Null;

Related

How to convert multiple varchar values into smallint

Pardon me, I was trying hard to find the answer, but most of the questions are related in the forum related to converting one value, not the whole set.
I am trying to pass the subquery values to the main query but the subquery returning varchar and the main query column is accepting smallint. I tried cast and convert but didn't help me.
select time_off_type_no
from schedtime
where activity_no in (select AT_NUMBERS from ACTIVITY where AT_ID = 105)
This query is throwing the following exception
Conversion failed when converting the varchar value '483,484,485,486,487,488,489' to data type smallint
Any advice on how to convert the values much appreciated.
Following query returning '483,484,485,486,487,488,489' and I want to convert all the values to SmallInt or int to pass it to the main query.
select AT_NUMBERS
from ACTIVITY
where AT_ID = 105
Please try nested casting like:
SELECT CAST(CAST(AT_NUMBERS AS DECIMAL) AS SMALLINT) from ACTIVITY where AT_ID=105
EDIT: Since the returned value is a comma-delimited string, I think this would help if the version of SQL Server is at least 2016
;with cte (ID) as (
Select string_split (AT_NUMBERS,',') as ID
from ACTIVITY
where AT_ID=105
)
select time_off_type_no from schedtime where activity_no in (
SELECT CAST(CAST(ID AS DECIMAL) AS SMALLINT) from cte
)
If SQL SERVER version is below 2016, we'll need to develop our own split function. You can find examples in How to split a comma-separated value to columns
Try this as an example if so, working in Sql Server 2008:
DECLARE #t TABLE
(
EmployeeID INT,
Certs VARCHAR(8000)
)
INSERT #t VALUES (1,'B.E.,MCA, MCDBA, PGDCA'), (2,'M.Com.,B.Sc.'), (3,'M.Sc.,M.Tech.')
SELECT EmployeeID,
LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) AS Certs
FROM
(
SELECT EmployeeID,CAST('<XMLRoot><RowData>' + REPLACE(Certs,',','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x
FROM #t
)t
CROSS APPLY x.nodes('/XMLRoot/RowData')m(n)
Ref: https://blog.sqlauthority.com/2015/04/21/sql-server-split-comma-separated-list-without-using-a-function/
I think you need to split the string by comma if these (483,484,485,486,487,488,489) are individual numbers. If this is whole integer value, not even Big Int limit is like this.
See MS documentation:
https://learn.microsoft.com/en-us/sql/t-sql/data-types/int-bigint-smallint-and-tinyint-transact-sql?view=sql-server-2017
If your SQL server version is more than 2016 or more, then you can use string_split function in this way.
--Use try_cast or Try_convert to avoid any conversion error as well.
select Try_cast(value as int) Integervalue from string_split ('483,484,485,486,487,488,489',',')
Output:
Integervalue
483
484
485
486
487
488
489
--this will work if it is pure integer value, else it needs to be converted to decimal.
Please make sure to use cross apply if you are using against tables.
If it is less than 2016, you might have to build one string split function as mentioned here.
Splitting the string in sql server

Setting a variable in a SQL WHERE clause to be used in SELECT

I'm using Transact-SQL with Microsoft SQL Server, and we have a query that looks like this:
SELECT Cast( Cast ( Cast(XMLBlob as XML).query(N'//continent/forest/tree/age/Text()') as nvarchar) as bigint),
AnotherField
FROM [MyDB].[dbo].[mytable]
WHERE Cast( Cast ( Cast(XMLBlob as XML).query(N'//continent/forest/tree/age/Text()') as nvarchar) as bigint)
between 10 and 100
The XML cast is an expensive operation, and since it's used in both the WHERE and SELECT, it seems like I should be able to save it away as a variable in the WHERE (which, by order of operations, is evaluated before the SELECT), and use it in the SELECT instead of having to cast again. Is this possible?
You could use an inner query where you retrieve the XML value. Then outside the inner query you both return that bigint value and filter the values you want:
SELECT innerTable.Age, innerTable.AnotherField
FROM (
SELECT Cast( Cast ( Cast(XMLBlob as
XML).query(N'//continent/forest/tree/age/Text()') as nvarchar) as bigint) AS Age,
AnotherField
FROM [MyDB].[dbo].[mytable]
) AS innerTable
WHERE innerTable.Age between 10 and 100
By the way... why do you need a bigint to store Age? If you are storing years looks like overkill, even for those trees that live thousands of years :)

Calculating SQL Average with non-numeric data in Table

I have this SQL query that is failing on this nvarchar, even though it seems to me that it is properly guarded for a cast to work. I am trying to get the average of the values for correctly formatted numerical values on a database that I only have read access to.
select TagName, count(TagName) as Freq,
sum(isnumeric(value)) as GoodNums,
avg(case isnumeric(value) when 1 then cast(value as numeric) else 0 end) as "Avg",
Min(Timestamp) as StartTime,
Max(Timestamp) as EndTime
from HH_Data_9 group by TagName
However I never really completely grokked the SQL syntax for combinations of CASE, aggregate functions, and GROUP_BY, so maybe I am just writing it wrong (Tried quite a few things before posting this). Note that the "GoodNums" column works and is giving reasonable answers but when I add the "Avg" column to the query the whole thing errors out of with:
Msg 8114, Level 16, State 5, Line 1
Error converting data type nvarchar to numeric.
This is Microsoft SQL Server 2014 by the way. Any ideas?
Just want to caution that this approach is very dangerous. Consider the following table structure:
CREATE TABLE TestTable
(
FieldType NVARCHAR(50) ,
FieldValue NVARCHAR(50)
)
GO
INSERT INTO dbo.TestTable
VALUES ( 'INT', '5' ),
( 'INT', '15' ),
( 'MONEY', '5.5' )
SELECT *
FROM dbo.TestTable
WHERE FieldType = 'INT'
AND CAST(FieldValue AS INT) > 10
On my machine this query works, but the thing here is that many assumes that this predicate will evaluate from left to right. But this is incorrect. SQL Server engine may decide to evaluate this expression from right to left. And in this case you will get Conversion failed error. It's called ALL-AT-ONCE principle. However in your example this is not applicable, but just wanted to mention.

Check if field is numeric, then execute comparison on only those field in one statement?

This may be simple, but I am no SQL whiz so I am getting lost. I understand that sql takes your query and executes it in a certain order, which I believe is why this query does not work:
select * from purchaseorders
where IsNumeric(purchase_order_number) = 1
and cast(purchase_order_number as int) >= 7
MOST of the purchar_order_number fields are numeric, but we introduce alphanumeric ones recently. The data I am trying to get is to see if '7' is greater than the highest numeric purchase_order_number.
The Numeric() function filters out the alphanumeric fields fine, but doing the subsequent cast comparison throws this error:
Conversion failed when converting the nvarchar value '124-4356AB' to data type int.
I am not asking what the error means, that is obvious. I am asking if there is a way to accomplish what I want in a single query, preferably in the where clause due to ORM constraints.
does this work for you?
select * from purchaseorders
where (case when IsNumeric(purchase_order_number) = 1
then cast(purchase_order_number as int)
else 0 end) >= 7
You can do a select with a subselect
select * from (
select * from purchaseorders
where IsNumeric(purchase_order_number) = 1) as correct_orders
where cast(purchase_order_number as int) >= 7
try this:
select * from purchaseorders
where try_cast(purchase_order_number as int) >= 7
have to check which column has numeric values only.
Currently, in a table every field is setted with nvarchar(max) Like tableName (field1 nvarchar(max),field2 nvarchar(max),field3 nvarchar(3)) and tableName has 25lac Rows.
But on manually Check Field2 Contain the numeric Values Only... How to Check With t-sql that in the Complete Column (Field2) has numeric Value or not/null value with Longest Length in the Column!

Oracle SQL Case statement with NULL values

I'm trying to create an Oracle query that will use a parameter based on the CommandName of a button.
So here's my query
SELECT id
FROM table
WHERE dateTime IS (CASE WHEN :CommandName = 'FIRST' THEN NULL ELSE NOT NULL END)
So I'm passing the parameter but it's not working - I'm just getting a Missing NULL keyword error
I basically don't want to have to write two separate queries based on the parameter that is input
Not even sure if this is possible or if this is the right way of doing it?
Any ideas?
You may want to read up on the CASE statement a bit more.
I believe you want the following code:
SELECT
id
FROM
table
WHERE
(
(dateTime IS NULL AND :CommandName = 'FIRST')
OR
(dateTime IS NOT NULL AND NVL(:CommandName, '') <> 'FIRST')
)
Without using dynamic SQL to return the string 'NULL' or 'NOT NULL' you could use two NVL() functions:
select id
from table
where NVL(dateTime,'FIRST') = NVL(:param,dateTime);
However, if dateTime is indexed (and Oracle can index NULLs), ths NVL() function will disable the index.
I think that
SELECT id
FROM table
WHERE (dateTime IS NULL AND :CommandName='FIRST') OR (dateTime IS NOT NULL AND :CommandName <> 'FIRST')
should do what you need.