T-SQL find only the columns which can be casted as INT - sql

I need to write an SELECT statement, that has a WHERE clause, where only the rows that can be CAST as INT will be selected, not BIGINT, but INT.
Is that possible
I am on SQL Server 20**
Right now I am doing something like this:
SELECT CAST(column as INT) FROM TABLE
WHERE ISNUMERIC(column) = 1
But then I get 'The conversion of the varchar value '275949275947' overflowed an int column'
There is alot of different data in the column - but I only need the INTs

SQL Server 2012:
select try_parse('1231321313' as int)
select try_parse('234242342341231321' as int)
SQL Server < 2012:
select case when ISNUMERIC('1231321313') = 1
and patindex('%[^0-9-]%', '1231321313') = 0
and cast('1231321313' as bigint) between -2147483648 and 2147483647
then cast('1231321313' as int) end
select case when ISNUMERIC('234242342341231321') = 1
and cast('234242342341231321' as bigint) between -2147483648 and 2147483647
then cast('234242342341231321' as int) end
edit: to deal with potential currency values ('$23424231321', '€23424231321' etc) you could also add patindex('%[^0-9-]%', '1231321313') = 0

DECLARE #Table TABLE (
[column] VARCHAR(200)
)
INSERT INTO #Table SELECT '1'
INSERT INTO #Table SELECT '154674'
INSERT INTO #Table SELECT '-2147483649'
INSERT INTO #Table SELECT '2147483648'
SELECT CAST([column] as INT) FROM #TABLE
WHERE ISNUMERIC([column]) = 1
AND CAST([column] as BIGINT)>=-2147483648
AND CAST([column] as BIGINT)<=2147483647

This will work on MS SQL 2012+:
DECLARE #t TABLE ( A INT, B NVARCHAR(50) )
INSERT INTO #t
VALUES ( 1, '1' ),
( 2, '275949275947' ),
( 2, 'cc' )
SELECT CAST(B AS INT) AS B
FROM #t
WHERE TRY_PARSE(
B AS INT) IS NOT NULL
Output:
B
1

Related

Looping through a column to check if a cast succeeds in SQL Server

I am trying to check if an entire column which is varchar and make sure it can be cast to float. I have a cursor portion like so:
DECLARE #CastFailed BIT
SET #CastFailed = (SELECT SUM(CASE WHEN TRY_CAST(#ColumnName AS FLOAT) IS NULL THEN 1
ELSE 0 END) AS CastResult)
-- Look at this
PRINT #CastFailed
IF #CastFailed > 0
BEGIN
PRINT 'ERROR: ' + #ColumnName + ' cannot be converted to FLOAT type'
SET #HasErrors = 1
END
ELSE
BEGIN
PRINT 'The cast has passed.'
END
For some reason, it is always returning 1. I already in a previous part of the cursor (not shown but above), verified that the column passed in (#ColumnName) is NOT NULL at any point.
I need to find out if all the CAST to FLOAT for #ColumnName are valid. The cursor loops through a table of columns bring in the FETCH #ColumnName one by one. What am I missing?
Easy:
DECLARE #t TABLE (txt VARCHAR(100));
INSERT #t VALUES ('ABC123'),('100.00'),('100'),('11.222.333'),('00');
DECLARE #CastFailed BIT =
(SELECT ISNULL(MAX(1),0) FROM #t AS t WHERE TRY_CAST(t.Txt AS FLOAT) IS NULL);
SELECT CastFailed = #CastFailed;
For even better performance ...
DECLARE #t TABLE (txt VARCHAR(100));
INSERT #t VALUES ('ABC123'),('100.00'),('100'),('11.222.333'),('00');
DECLARE #CastFailed BIT =
(ISNULL((SELECT TOP(1) 1 FROM
(SELECT 1 FROM #t AS t WHERE TRY_CAST(t.Txt AS FLOAT) IS NULL) AS x(x)),0));
SELECT CastFailed = #CastFailed;
Here is an option where you can avoid cursors and Dynamic SQL. It will dynamically UNPIVOT your data and return the columns which fail the conversion to float
(2008 & 2012 Compatible)
Example
Declare #YourTable Table (id int,[Col1] varchar(50),[Col2] varchar(50))
Insert Into #YourTable Values
(1,'1e6','ABC') -- This Col2 will fail Conversion
,(2,'5.5','25')
,(3,'50.25','0')
Select C.Col
,Failed = count(*)
from #YourTable A
Cross Apply ( values ( convert(xml,(Select A.* for XML RAW)) ) )B(XMLData)
Cross Apply (
Select Col = xAttr.value('local-name(.)', 'varchar(100)')
,Value = xAttr.value('.','varchar(max)')
From XMLData.nodes('//#*') xNode(xAttr)
) C
Where Col in ('Col1','Col2') -- Or you can Exclude Columns ... Where Col NOT in ('id','OtherCols','ToExclude')
and try_convert(float,value) is null
Group BY C.Col
Results
Col Failed
Col2 1

Search list of values including range in SQL using WHERE IN clause with SQL variable?

I am trying to implement search functionality with list of values in SQL variable, including range. Appreciate any guidance/links pointing to correct approach for this.
Below is the dataset:
CREATE TABLE [dbo].[Books]
(
[ID] [NCHAR](10) NOT NULL,
[AUTHCODE] [NCHAR](10) NULL,
[TITLE] [NCHAR](10) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[Books] ([ID], [AUTHCODE], [TITLE])
VALUES (N'1', N'nk', N'Book1'),
(N'2', N'an', N'Book2'),
(N'3', N'mn', N'Book3'),
(N'4', N'ra', N'Book4'),
(N'5', N'kd', N'Book5'),
(N'6', N'nk', N'Book6'),
(N'7', N'an', N'Book7'),
(N'8', N'ra', N'Book8'),
(N'9', N'kd', N'Book9'),
(N'10', N'mn', N'Book10 ')
GO
Below I am trying to filter using the SQL IN clause but this does not return desired result.
select * from books
declare #List1 varchar(max) = '2,4,6,7,8,9' --simple list
select *
from books
where id in (#List1)
declare #List2 varchar(max) = '2,4-7,9' --list with range
select *
from books
where id in (#List2)
You cannot directly use strings as lists, but you can do use STRING_SPLIT (Transact-SQL) if you really need to pass filtering parameters as strings:
declare #list varchar(max) = '2,4,6-8,9'
declare #filter table (id1 int, id2 int)
insert into #filter (id1,id2)
select
case when b.pos > 0 then left(a.[value], pos - 1) else a.[value] end as id1,
case when b.pos > 0 then right(a.[value], len(a.[value]) - pos) else a.[value] end as id2
from string_split(#list, ',') as a
cross apply (select charindex('-', a.[value]) as pos) as b
select *
from [dbo].[Books] as b
where
exists (select * from #filter as tt where b.id between tt.id1 and tt.id2)
Also it might be an idea to pass your filter as json and OPENJSON (Transact-SQL) so you can make parsing part simplier:
declare #list varchar(max) = '[2,4,[6,8],9]'
select
case when a.[type] = 4 then json_value(a.[value], '$[0]') else a.[value] end,
case when a.[type] = 4 then json_value(a.[value], '$[1]') else a.[value] end
from openjson(#list) as a
All above, of course, only applicable if you have Sql Server 2016 or higher
IN() operator determines whether a specified value matches any value in a subquery or a list. not a string and that what you are doing.
What you are trying to do can be done as
DECLARE #List1 AS TABLE (ID INT);
INSERT INTO #List1
SELECT 2 UNION SELECT 4 UNION SELECT 6 UNION
SELECT 7 UNION SELECT 8 UNION SELECT 9;
SELECT *
FROM Books
WHERE ID IN (SELECT ID FROM #List1);
DECLARE #List2 As TABLE (AFrom INT, ATo INT);
INSERT INTO #List2
SELECT 2, 4 UNION SELECT 7, 9;
SELECT *
FROM Books B CROSS APPLY #List2 L
WHERE B.ID BETWEEN L.AFrom AND L.ATo;
Live Demo

dynamic alias in sql server

I want query field with different alias in stored procedure
select COUNT(EmpCode) as CountEmp+#para
result shoud be
CountEmp1
45
CountEmp2
54
CountEmp1
76
Query loop in c# code:
select COUNT(EmpCode) where something = #something as CountEmp+#para
Approach without dynamic SQL:
--I create temp table for demonstration
DECLARE #some_table TABLE (
Something int,
EmpCode INT
)
INSERT INTO #some_table (Something, EmpCode)
VALUES (1, 10),(1, 22),(1, 12),(2, 12),(2, 30),(3, 65),(3, 15),(3, 11),(3, 5)
--Declare parameter we want to search
DECLARE #param int = 1
--Query
--In cte we select what we need based on parameter
;WITH cte AS (
SELECT 'CountEmp'+CAST(#param as nvarchar(10)) as SomeThing,
CAST(COUNT(EmpCode) as nvarchar(10)) as EmpCodeCount,
ROW_NUMBER() OVER (ORDER BY SomeThing ) as rn
FROM #some_table
WHERE SomeThing = #param
GROUP BY SomeThing
)
--And here comes UNION
SELECT SomeThing as Result
FROM (
SELECT SomeThing,rn
FROM cte
UNION ALL
SELECT EmpCodeCount ,rn
FROM cte
) as t
ORDER BY rn, SomeThing DESC
Output:
Result
------------------
CountEmp1
3
(2 row(s) affected)
Please try to make use of below code. Its working fine with SQL Server 2012.
IF OBJECT_ID ('temp..#Mytable') IS NOT NULL
CREATE TABLE #Mytable (ID INT IDENTITY (1,1),EmpCode INT)
DECLARE #max int ,#count int
SET #max =0;
DECLARE #str varchar(10)
INSERT #Mytable
(EmpCode)
VALUES
(10),
(45),
(35),
(63),
(56),
(65)
SET #count = (SELECT COUNT (ID) FROM #Mytable )
WHILE #count > #max
BEGIN
SET #max = #max+1
SET #str = CONVERT(varchar(10),#max)
EXEC('SELECT EmpCode AS Empcode'+#str+ ' FROM #Mytable WHERE ID = '+#str)
END

select records that one column are numbers close to 10

I have a table with 3 columns.
one of them is [Code]. I have many records on this table.
I want to select records that their [Code] are numbers close to 10 regularly
for example if select records that has [Code]=9 then select records that has [Code] = 8 etc...
This is what I implement based on your though.
If you wish near record or record-id, not value, then you can change only condition a.data to a.rid.
declare #t table (data int)
insert into #t values(1), (2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(50),(51),(52)
declare #value int = 11 , #getDatToValue int = 2
select * from
(
select * , ROW_NUMBER( ) over(order by data) rid
from #t
)
a
where
a.data between (#value - #getDatToValue) and (#value + #getDatToValue)

t-sql find specific value with a csv string

I need some on help a SQL Query. I have a column with values stored as comma separated values.
I need to write a query which finds the 3rd delimited item within each value in the column.
Is this possible to do this in a Select statement?
ex: ColumnValue: josh,Reg01,False,a0-t0,22/09/2010
So I will need to get the 3rd value (i.e.) False from the above string.
Yes.
Where #s is your string...
select
SUBSTRING (#s,
CHARINDEX(',',#s,CHARINDEX(',',#s)+1)+1,
CHARINDEX(',',#s,CHARINDEX(',',#s,CHARINDEX(',',#s)+1)+1)
-CHARINDEX(',',#s,CHARINDEX(',',#s)+1)-1)
Or more generically...
;with cte as
(
select 1 as Item, 1 as Start, CHARINDEX(',',#s, 1) as Split
union all
select cte.Item+1, cte.Split+1, nullif(CHARINDEX(',',#s, cte.Split+1),0) as Split
from cte
where cte.Split<>0
)
select SUBSTRING(#s, start,isnull(split,len(#s)+1)-start)
from cte
where Item = 3
Now store your data properly :)
Try this (assuming SQL Server 2005+)
DECLARE #t TABLE(ColumnValue VARCHAR(50))
INSERT INTO #t(ColumnValue) SELECT 'josh,Reg01,False,a0-t0,22/09/2010'
INSERT INTO #t(ColumnValue) SELECT 'mango,apple,bannana,grapes'
INSERT INTO #t(ColumnValue) SELECT 'stackoverflow'
SELECT ThirdValue = splitdata
FROM(
SELECT
Rn = ROW_NUMBER() OVER(PARTITION BY ColumnValue ORDER BY (SELECT 1))
,X.ColumnValue
,Y.splitdata
FROM
(
SELECT *,
CAST('<X>'+REPLACE(F.ColumnValue,',','</X><X>')+'</X>' AS XML) AS xmlfilter FROM #t F
)X
CROSS APPLY
(
SELECT fdata.D.value('.','varchar(50)') AS splitdata
FROM X.xmlfilter.nodes('X') as fdata(D)
) Y
)X WHERE X.Rn = 3
//Result
ThirdValue
False
bannana
Also it is not very clear from your question as what version of SQL Server you are using. In case you are using SQL SERVER 2000, you can go ahead with the below approach.
Step 1: Create a number table
CREATE TABLE dbo.Numbers
(
N INT NOT NULL PRIMARY KEY
);
GO
DECLARE #rows AS INT;
SET #rows = 1;
INSERT INTO dbo.Numbers VALUES(1);
WHILE(#rows <= 10000)
BEGIN
INSERT INTO dbo.Numbers SELECT N + #rows FROM dbo.Numbers;
SET #rows = #rows * 2;
END
Step 2: Apply the query below
DECLARE #t TABLE(ColumnValue VARCHAR(50))
INSERT INTO #t(ColumnValue) SELECT 'josh,Reg01,False,a0-t0,22/09/2010'
INSERT INTO #t(ColumnValue) SELECT 'mango,apple,bannana,grapes'
INSERT INTO #t(ColumnValue) SELECT 'stackoverflow'
--Declare a table variable to put the identity column and store the indermediate results
DECLARE #tempT TABLE(Id INT IDENTITY,ColumnValue VARCHAR(50),SplitData VARCHAR(50))
-- Insert the records into the table variable
INSERT INTO #tempT
SELECT
ColumnValue
,SUBSTRING(ColumnValue, Numbers.N,CHARINDEX(',', ColumnValue + ',', Numbers.N) - Numbers.N) AS splitdata
FROM #t
JOIN Numbers ON Numbers.N <= DATALENGTH(ColumnValue) + 1
AND SUBSTRING(',' + ColumnValue, Numbers.N, 1) = ','
--Project the filtered records
SELECT ThirdValue = X.splitdata
FROM
--The co-related subquery does the ROW_NUMBER() OVER(PARTITION BY ColumnValue)
(SELECT
Rn = (SELECT COUNT(*)
FROM #tempT t2
WHERE t2.ColumnValue=t1.ColumnValue
AND t2.Id<=t1.Id)
,t1.ColumnValue
,t1.splitdata
FROM #tempT t1)X
WHERE X.Rn =3
-- Result
ThirdValue
False
bannana
Also you can use Master..spt_Values for your number table
DECLARE #t TABLE(ColumnValue VARCHAR(50))
INSERT INTO #t(ColumnValue) SELECT 'josh,Reg01,False,a0-t0,22/09/2010'
INSERT INTO #t(ColumnValue) SELECT 'mango,apple,bannana,grapes'
INSERT INTO #t(ColumnValue) SELECT 'stackoverflow'
--Declare a table variable to put the identity column and store the indermediate results
DECLARE #tempT TABLE(Id INT IDENTITY,ColumnValue VARCHAR(50),SplitData VARCHAR(50))
-- Insert the records into the table variable
INSERT INTO #tempT
SELECT
ColumnValue
,SUBSTRING(ColumnValue, Number ,CHARINDEX(',', ColumnValue + ',', Number ) - Number) AS splitdata
FROM #t
JOIN master..spt_values ON Number <= DATALENGTH(ColumnValue) + 1 AND type='P'
AND SUBSTRING(',' + ColumnValue, Number , 1) = ','
--Project the filtered records
SELECT ThirdValue = X.splitdata
FROM
--The co-related subquery does the ROW_NUMBER() OVER(PARTITION BY ColumnValue)
(SELECT
Rn = (SELECT COUNT(*)
FROM #tempT t2
WHERE t2.ColumnValue=t1.ColumnValue
AND t2.Id<=t1.Id)
,t1.ColumnValue
,t1.splitdata
FROM #tempT t1)X
WHERE X.Rn =3
You can read about this from
1) What is the purpose of system table table master..spt_values and what are the meanings of its values?
2) Why (and how) to split column using master..spt_values?
You really need something like String.Split(',')(2) which unfortunately dos not exist in SQL but this may be helpful to you
You can make some test with this solution and the other ones but, I believe that using XML in such situations almost always gives to you best performance and insure less coding:
DECLARE #InPutCSV NVARCHAR(2000)= 'josh,Reg01,False,a0-t0,22/09/2010'
DECLARE #ValueIndexToGet INT=3
DECLARE #XML XML = CAST ('<d>' + REPLACE(#InPutCSV, ',', '</d><d>') + '</d>' AS XML);
WITH CTE(RecordNumber,Value) AS
(
SELECT ROW_NUMBER() OVER(ORDER BY T.v.value('.', 'NVARCHAR(100)') DESC) AS RecordNumber
,T.v.value('.', 'NVARCHAR(100)') AS Value
FROM #XML.nodes('/d') AS T(v)
)
SELECT Value
FROM CTE WHERE RecordNumber=#ValueIndexToGet
I can confirm that it takes 1 seconds to get value from CSV string with 100 000 values.