How to extract numeric values from a string in SQL Server - sql-server

I have a string in a SQL Server table whose format is like this...
nvarchar int nvarchar int nvarchar
There are no obvious delimiters other than some chars are numeric and others are alpha.
How do I reference the second int value?

One way is to use the patindex function:
declare #s varchar(100)
declare #i1 int
declare #s2 varchar(100)
declare #i2 int
declare #s3 varchar(100)
declare #i3 int
declare #s4 varchar(100)
declare #i4 int
declare #secondInt int
set #s = 'alpha123beta3140gamma789'
set #i1 = PATINDEX('%[0-9]%', #s)
set #s2 = SUBSTRING(#s, #i1, 100)
set #i2 = PATINDEX('%[^0-9]%', #s2)
set #s3 = SUBSTRING(#s2, #i2, 100)
set #i3 = PATINDEX('%[0-9]%', #s3)
set #s4 = SUBSTRING(#s3, #i3, 100)
set #i4 = PATINDEX('%[^0-9]%', #s4)
set #secondInt = CAST(SUBSTRING(#s4, 1, #i4-1) as int)
select #s, #secondInt

I would personally write a CLR function and use the string SPLIT function.
Here is some code that I believe works:
Declare #Result Table
(
stringval varchar(100),
numvalue decimal(18,4)
)
Declare #Test varchar(100)
Declare #index int
Declare #char char(1)
Declare #currentVal varchar(100)
Declare #prevVal varchar(100)
Declare #currentType char(1)
Declare #nextType char(1)
Set #index = 0
Set #Test = 'a100.4bb110ccc2000'
Set #currentVal = ''
Set #currentType = 's'
While #index <= LEN(#Test)
Begin
Set #index = #index + 1
Set #char = SUBSTRING(#Test,#index,1)
Set #nextType = CASE WHEN PATINDEX('[^0-9.]', #char) > 0 then 's' else 'n' end
If #currentType <> #nextType
begin
if #currentType = 'n'
insert into #Result(stringval,numvalue) values(#prevVal,#currentVal)
Set #prevVal = #currentVal
set #currentVal = ''
set #currentType = #nextType
end
SEt #currentVal = #currentVal + #char
ENd
Select * FROM #Result

This article on using Regular Expressions with SQL Server may be helpful.
Regular Expressions Make Pattern Matching And Data Extraction Easier

Related

Find word in a string with a character

I have tried so many times but could not find the exact query yet.
The one I made works in few string but doesn't work in another(It is uncertain).
What i want is the word which has '.' in it like "abcde sfjhjk.dkjb sajb njdhf", what i want is "sfjhjk.dkjb" as result . This is just an example.
The query returns all letters in some cases while truncates few digits in other cases. You can check by providing different values.
I tried below :
This doesn't work:
DECLARE #QUERY VARCHAR(MAX)='
renewreque0_.amount AS AMOUNT48_,
renewreque0_.charge_type AS CHARGE3_48_,
renewreque0_.commission_rate AS COMMISSION4_48_
'
SET NOCOUNT ON;
DECLARE #TABLENAME TABLE(TABLE_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
DECLARE #COLUMNS_JOIN TABLE(COL VARCHAR(MAX),COLUMN_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
DECLARE #NAME VARCHAR(MAX),#ALIAS VARCHAR(MAX),#J_QUERY VARCHAR(MAX),#W_QUERY VARCHAR(MAX)
DECLARE #WHERE_JOIN TABLE(COL VARCHAR(MAX),COLUMN_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
WHILE CHARINDEX('.',#QUERY)>1
BEGIN
SET #NAME = REVERSE( SUBSTRING(REVERSE(#QUERY),CHARINDEX('.',REVERSE(#QUERY))+1,CHARINDEX(' ',#QUERY)) )
SET #ALIAS= REVERSE(LEFT(REVERSE(#QUERY),CHARINDEX('.',REVERSE(#QUERY))))
SET #ALIAS=LEFT(#ALIAS,CHARINDEX(' ',#ALIAS))
SET #NAME=LTRIM(RTRIM(#NAME))
SET #ALIAS=LTRIM(RTRIM(#ALIAS))
INSERT INTO #COLUMNS_JOIN SELECT #NAME+#ALIAS,#NAME,REVERSE(LEFT(REVERSE(#ALIAS),LEN(#ALIAS)-1))
SET #QUERY=REPLACE(#QUERY,#NAME+#ALIAS,'')
END
SELECT * FROM #COLUMNS_JOIN
This works:
DECLARE #QUERY VARCHAR(MAX)='
AND t8_.username LIKE ?
AND t4_.branch_id = ?
AND t1_.account_no = ?
AND t0_.remarks = ?
AND t0_.collect_from = ?
'
SET NOCOUNT ON;
DECLARE #TABLENAME TABLE(TABLE_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
DECLARE #COLUMNS_JOIN TABLE(COL VARCHAR(MAX),COLUMN_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
DECLARE #NAME VARCHAR(MAX),#ALIAS VARCHAR(MAX),#J_QUERY VARCHAR(MAX),#W_QUERY VARCHAR(MAX)
DECLARE #WHERE_JOIN TABLE(COL VARCHAR(MAX),COLUMN_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
WHILE CHARINDEX('.',#QUERY)>1
BEGIN
SET #NAME = REVERSE( SUBSTRING(REVERSE(#QUERY),CHARINDEX('.',REVERSE(#QUERY))+1,CHARINDEX(' ',#QUERY)) )
SET #ALIAS= REVERSE(LEFT(REVERSE(#QUERY),CHARINDEX('.',REVERSE(#QUERY))))
SET #ALIAS=LEFT(#ALIAS,CHARINDEX(' ',#ALIAS))
SET #NAME=LTRIM(RTRIM(#NAME))
SET #ALIAS=LTRIM(RTRIM(#ALIAS))
INSERT INTO #COLUMNS_JOIN SELECT #NAME+#ALIAS,#NAME,REVERSE(LEFT(REVERSE(#ALIAS),LEN(#ALIAS)-1))
SET #QUERY=REPLACE(#QUERY,#NAME+#ALIAS,'')
END
SELECT * FROM #COLUMNS_JOIN
Can anybody please help.
I would first use an SplitString function (passing a blank space as delimiter), which returns as rows each word on a string, and then filter it to return just the words having a dot.
SQL Server 2016 already has one https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql and on older SQL Servers you can build your own : Splitting the string in sql server
set #SQLStr varchar(512) = 'abcde sfjhjk.dkjb sajb njdhf';
select value from string_split(#SQLStr, ' ')
where charindex('.', value) > 0
Alright just for fun
declare #str nvarchar(100) = 'abcde sfjhjk.dkjb sajb njdhf',
#pointIndex int
SET #pointIndex = (SELECT CHARINDEX('.',#str) )
SELECT RTRIM(LTRIM(SUBSTRING(#str, #pointIndex - CHARINDEX(' ',REVERSE(LEFT(#str,#pointIndex))) +1,CHARINDEX(' ',REVERSE(LEFT(#str,#pointIndex)))) -- left side
+SUBSTRING(#str,#pointIndex +1, CHARINDEX( ' ', SUBSTRING(#str,#pointIndex,len(#str) - #pointIndex) ) -1 )))
Needless to say i would not recommend this option because it is really hard to maintain. As Marc said your best option here is to split your string for blanks and find the ones with a '.'
Now if you dont have SQLServer 2016 here is a split function for you :
CREATE function [dbo].[Split]
(
#string nvarchar(max),
#delimiter nvarchar(20)
)
returns #table table
(
[Value] nvarchar(max)
)
begin
declare #nextString nvarchar(max)
declare #pos int, #nextPos int
set #nextString = ''
set #string = #string + #delimiter
set #pos = charindex(#delimiter, #string)
set #nextPos = 1
while (#pos <> 0)
begin
set #nextString = substring(#string, 1, #pos - 1)
insert into #table
(
[Value]
)
values
(
#nextString
)
set #string = substring(#string, #pos + len(#delimiter), len(#string))
set #nextPos = #pos
set #pos = charindex(#delimiter, #string)
end
return
end
And use it as such :
SELECT * FROM dbo.Split(REPLACE(#str,' ','/'),'/')
WHERE charindex('.', value) > 0
Note that i replace blanks by another value.

Conversion failed when converting the varchar value ',1,2,3' to data type int

This error results when attempting to use a comma delimited parameter in an IN condition
I'm passing a varchar parameter to a stored procedure that looks like this
,1,2,3
And I want to find out if it contains 1 (it doesn't always contain 1)
What's the easiest way to do that in TSQL ?
declare #Nums varchar(max)=',1,2,3'
if 1 in (#Nums) -- conversion error
BEGIN
select * from TestTable
END
You will need to use LIKE to see if the string contains the character 1. Note this will also match 12 or any string with the character '1' in it.
declare #Nums varchar(max)=',1,2,3'
if #Nums LIKE '%1%'
BEGIN
select * from TestTable
END
If you need to match the full number:
CREATE FUNCTION [dbo].[Split_String]
(
#ItemList NVARCHAR(4000),
#delimiter CHAR(1)
)
RETURNS #IDTable TABLE (Item VARCHAR(50))
AS
BEGIN
DECLARE #tempItemList NVARCHAR(4000)
SET #tempItemList = #ItemList
DECLARE #i INT
DECLARE #Item NVARCHAR(4000)
SET #tempItemList = REPLACE (#tempItemList, ' ', '')
SET #i = CHARINDEX(#delimiter, #tempItemList)
WHILE (LEN(#tempItemList) > 0)
BEGIN
IF #i = 0
SET #Item = #tempItemList
ELSE
SET #Item = LEFT(#tempItemList, #i - 1)
INSERT INTO #IDTable(Item) VALUES(#Item)
IF #i = 0
SET #tempItemList = ''
ELSE
SET #tempItemList = RIGHT(#tempItemList, LEN(#tempItemList) - #i)
SET #i = CHARINDEX(#delimiter, #tempItemList)
END
RETURN
END
DECLARE #Nums VARCHAR(MAX) = ',1,2,3'
DECLARE #NumberTable TABLE (item INT)
INSERT INTO #NumberTable
SELECT TRY_CAST(Item AS INT)
FROM dbo.Split_String(#Nums, ',')
IF (SELECT 1 FROM #NumberTable WHERE item = 1) = 1
BEGIN
select * from TestTable
END
You can use CHARINDEX.
declare #Nums varchar(max)=',1,2,3'
IF CHARINDEX(',1,', #Nums+',') > 0
BEGIN
select * from TestTable
END

Transform text in SQL Server

I am trying to create a dynamic query in SQL Server.
Input: #value= abc,def,en,
Output: MAX(abc) as abc, MAX(def) as def, MAX(en) as en
My efforts so far took me no where.
With CONVERT() and REPLACE() I achieved a bit but finding it difficult. Need help!
Try this:
declare #value varchar(50) = 'abc,def,en'
declare #result varchar(100) = ''
select #result = replace(#value,'abc', 'MAX(''abc'') as abc')
select #result = replace(#result,'def', 'MAX(''def'') as def')
select #result = replace(#result,'en', 'MAX(''en'') as en')
select #result
You can also do the replacements in one line by nesting the expressions.
EDIT: If you have variable values in #value, you can take the below approach:
Use a splitter function to get the individual values in the string as a list. You can take a look at this article for implementations.
Insert this list to a temp table.
Update the temp table as shown above.
Concatenate the values into a single string using STUFF like so:
select stuff((select ',' + val from #temp for xml path('')),1,1,'')
Try this:
DECLARE #Value VARCHAR(200) = 'abc,def,en'
DECLARE #Template VARCHAR(100) = 'MAX(''##'') as ##'
DECLARE #Result VARCHAR(1000) = ''
DECLARE #Data VARCHAR(100) = ''
WHILE LEN(#Value) > 0
BEGIN
SET #Data = REPLACE(LEFT(#Value, ISNULL(NULLIF(CHARINDEX(',', #Value),0), LEN(#Value))),',','')
SET #Result = #Result + REPLACE(#Template, '##', #Data)
IF CHARINDEX(',', #Value) > 0
BEGIN
SET #Result = #Result + ','
SET #Value = REPLACE(#Value,#Data + ',','')
END
ELSE
SET #Value = REPLACE(#Value,#Data,'')
END
SELECT #Result
Have a look at SQL User Defined Function to Parse a Delimited String
So you can do like
Declare #Value varchar(200) = 'abc,def,en'
Declare #Item varchar(20) = null
declare #Str varchar(1000)=''
WHILE LEN(#Value) > 0
BEGIN
IF PATINDEX('%,%',#Value) > 0
BEGIN
SET #Item = SUBSTRING(#Value, 0, PATINDEX('%,%',#Value))
-- SELECT #Item
IF(LEN(#Str)>0)
SET #Str = #Str + ', SELECT MAX('+#Item+') as ' +#Item
ELSE
SET#Str = #Str + ' SELECT MAX('+#Item+') as ' +#Item
SET #Value = SUBSTRING(#Value, LEN(#Item + ',') + 1, LEN(#Value))
END
ELSE
BEGIN
SET #Item = #Value
SET #Value = NULL
SET #Str = #Str + 'SELECT MAX('+#Item+') as ' + #Item
END
END
select #Str
See the fiddle sample here

How to get "singular" values in WHERE clause from a string?

I have a variable #text varchar which has some values separated by a symbol, whatever I chose it to be. Ex:
declare #text varchar
set #text='John^Marry^Smith^Ane^Sue^'
I need to delete some data, but because it is a different server and database (a very long story), I must specify the in the WHERE clause, the values from my string, something like this:
Delete Employers where employer_name in ('John','Marry','Smith','Ane','Sue')
Can this be done? Most of all without any other objects, like procedures or functions?
Best regards, Bogdan
Simplest way: generate your SQL query as a string, using replace to form your in list, then execute it.
declare #sqlquery nvarchar(max)
set #sqlquery = 'Delete Employers where employer_name in (''' + replace(#text, '^', ''',''') + ''')'
EXEC sp_executesql #sqlquery
IF I understand your question correctly, then the answer is yes, just at you have stated it. You can use the following strip to turn a string into a table. If you declare the return table as a table variable, then you can roll it into your script as a
DELETE where EXISTS(....)
Create function [dbo].[atf_BarListToTable]
(#list ntext)
RETURNS #tbl TABLE (ListPosn int IDENTITY(1, 1) NOT NULL,
SString VARCHAR(1028) NOT NULL) AS
BEGIN
DECLARE #pos int
DECLARE #textpos int
DECLARE #ChunkLength smallint
DECLARE #str nvarchar(4000)
DECLARE #tmpstr nvarchar(4000)
DECLARE #leftover nvarchar(4000)
SET #textpos = 1
SET #leftover = ''
WHILE #textpos <= datalength(#list) / 2
BEGIN
SET #ChunkLength = 4000 - datalength(#leftover) / 2
SET #tmpstr = ltrim(#leftover + substring(#list, #textpos, #ChunkLength))
SET #textpos = #textpos + #ChunkLength
SET #pos = charindex('|', #tmpstr)
WHILE #pos > 0
BEGIN
SET #str = substring(#tmpstr, 1, #pos - 1)
INSERT #tbl (SString) VALUES( #str)
SET #tmpstr = ltrim(substring(#tmpstr, #pos + 1, len(#tmpstr)))
SET #pos = charindex('|', #tmpstr)
END
SET #leftover = #tmpstr
END
IF ltrim(rtrim(#leftover)) <> ''
INSERT #tbl (SString) VALUES(#leftover)
RETURN
END

SQL Server Function

What is wrong with this function... The function intends to remove the specified leading characters from a given string. I know there is a patindex base solution to this which however doesn't consider spaces and all zero entries... but I want to know what is wrong with this one...
If I input "00012345" it should out put me "12345" however the output I'm getting is "0001234".. Why?
The test data is:
DECLARE #result varchar(max)
EXEC #result = TrimLeadingChar '00012345'
PRINT #result
The function code is:
CREATE FUNCTION TrimLeadingChar
(
#st AS Varchar(max),
#trimChar AS Varchar(1) = "0"
)
RETURNS Varchar(max)
AS
BEGIN
DECLARE #index int
DECLARE #temp Varchar(1)
SET #index = 0
if LEN(RTRIM(LTRIM(#st))) <= 1
return #st;
While(#index < LEN(#st))
BEGIN
set #temp = substring(#st,#index,1)
if #temp = #trimChar
SET #index = #index + 1
else
Break;
END
Return substring(#st,#index, LEN(#st))
END
GO
set #temp = substring(#st,#index+1,1)
instead of
set #temp = substring(#st,#index,1)
UPDATE:
OR you should set #index = 1 at first
DECLARE #index int
DECLARE #temp Varchar(1)
SET #index = 1
then
set #temp = substring(#st,#index,1)
Just for the sake of other users: Here is the working and complete solution function for SQL Server:
CREATE FUNCTION TrimBothEndsAndRemoveLeadingChar
(
#st AS VARCHAR(MAX),
#trimChar AS VARCHAR(1) = "0"
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #index INT
DECLARE #temp VARCHAR(1)
IF #st IS NULL OR #trimChar IS NULL OR LEN(RTRIM(LTRIM(#st))) <= 0
RETURN #st
SET #st = LTRIM(RTRIM(#st))
SET #index = 1
WHILE(#index <= LEN(#st))
BEGIN
SET #temp = SUBSTRING(#st, #index, 1)
IF #temp = #trimChar
SET #index = #index + 1
ELSE
BREAK;
END
DECLARE #result VARCHAR(MAX)
IF #index = (LEN(#st) + 1)
SET #result = #st
ELSE
SET #result = SUBSTRING(#st, #index, LEN(#st) + 1)
RETURN #result
END
GO

Resources