How can we read a varchar column, take the integer part out and add new column incrementing that integer part using script - sql

I need to write a SCRIPT for below scenario:
We have a column X with rows value for this column X as X01,X02,X03,X04........
The problem I am stuck with is that I needed to add another row to this table based on the value of the last row that is X04, Well I am able to identify the logic that I need to work which is given below:
I need to read value X04
Take the integer part 04
Increment by 1 => 05
Save column value as X05
I am able to pass with the 1st step which is not very hard. The problem that I am facing is the next steps. I have researched and tried quite a lot commands but none worked.
Any help is highly appreciated. Thanks.

You seem to be describing:
select concat(left(max(x), 1),
right(concat('00', try_convert(int, right(max(x), 2)) + 1), 2)
from t;
This is doing the following:
Taking the left most character.
Converting the two right characters to a number and adding one.
Converting that back to a zero-padded string.
Here is a db<>fiddle.
Now: That you want to increment a string value seems broken. You should just use an identity column or sequence to assign a number. You can format the value as a string when you query the table -- or use a computed column to store that.

Try below Script
CREATE TABLE #table (x varchar(20))
INSERT INTO #table VALUES('X01'),('X02'),('X03'),('X04')
DECLARE #maxno NVARCHAR(20)
DECLARE #maxstring NVARCHAR(20)
DECLARE #finalno NVARCHAR(20)
DECLARE #loopminno INT =1 -- you can change based on the requirement
DECLARE #loopmaxno INT =10 -- how many number we want to increment
WHILE #loopminno < #loopmaxno
BEGIN
select #maxno = MAX(CAST(SUBSTRING(x, PATINDEX('%[0-9]%', x), 100) as INT))
, #maxstring = MAX(SUBSTRING(x, 1, PATINDEX('%[0-9]%',x)-1))
from #table
where PATINDEX('%[1-9]%',x)>0
SELECT #finalno = #maxstring + CASE WHEN CAST(#maxno AS INT)<9 THEN '0' ELSE '' END + CAST(#maxno+1 AS VARCHAR(20))
INSERT INTO #table
SELECT #finalno
SET #loopminno = #loopminno+1
END

Related

Needing to parse out data

I am trying to parse out certain data from a string and I am having issues.
Here is the string:
1=BETA.1.0^2=175^3=812^4=R^5=N^9=1^12=1^13=00032^14=REP NOT FOUND ON REP TABLE, CANNOT INSERT TO REPRGR.^10=107~117~265~1114~3143~3505~3506~3513~5717^11=SA16~1~WY~WY~A~S~20100210~001~SE62^-omitted due to existing Rep Not Found
I need to return this "REP NOT FOUND ON REP TABLE, CANNOT INSERT TO REPRGR."
Here is my query SELECT CONVERT(VARCHAR(5000),CHARINDEX('14=',Column))FROM Table
If you're parsing, can we assume that you don't know what might come after the '^14=', but you need to capture whatever does? So searching for a particular string won't work because anything could come after '^14='. The best approach is to identify the longest reliable specific string that gives you a "foothold" to find the data you're looking for. What you don't want to do is accidentally capture the wrong data if the '^14=' appears more than once in your string. It looks like the '^' is your delimiter, since I don't see one at the start of the string. So you were actually on the right track, you just need to use SUBSTRING as a commenter mentioned. You also need to identify a marker for the end of the error message, which looks like it might be the next occurring '^', correct? Check several samples to be sure of this, and make sure the end marker doesn't at any point exist before your start marker or you'll get an error.
SELECT CAST((SUBSTRING(Column,CHARINDEX('14=',Column,0),CHARINDEX('^',Column,CHARINDEX('14=',Column,0) + 1) - CHARINDEX('14=',Column,0))) AS VARCHAR(5000)) FROM Table
You may need to increment or decrement the start position and end position by doing a +1 or -1 to fully capture your error message. But this should dynamically grab any length error message provided you are positive of your starting and ending markers.
I also have here a table-valued parsing function, where you would pass it the string and the '^' and it will return a table of data with not only the 14=, but everything.
CREATE function [dbo].[fn_SplitStringByDelimeter]
(
#list nvarchar(8000)
,#splitOn char(1)
)
returns #rtnTable table
(
id int identity(1,1)
,value nvarchar(100)
)
as
begin
declare #index int
declare #string nvarchar(4000)
select #index = 1
if len(#list) < 1 or #list is null return
--
while #index!= 0
begin
set #index = charindex(#splitOn,#list)
if #index!=0
set #string = left(#list,#index - 1)
else
set #string = #list
if(len(#string)>0)
insert into #rtnTable(value) values(#string)
--
set #list = right(#list,len(#list) - #index)
if len(#list) = 0 break
end
return
end
It sounds like you're trying to get the value of argument 14. This should do it:
select substring(
someData
, charindex('^14=',someData) + 4
, charindex('^',someData, charindex('^14=',someData) + 4) - charindex('^14=',someData) - 4
) errorMessage
from myData
where charindex('^14=',someData) > 0
and charindex('^',someData, charindex('^14=',someData) + 4) > 0
Try it here: http://sqlfiddle.com/#!18/22f23/2
This gets a substring of the given input.
The substring starts at the first character after the string ^14=; i.e. we get the index of ^14= in the string, then add 4 to it to skip over the matched characters themselves.
The substring ends at the first ^ character after the one in ^14=. We get the index of that character, then subtract the starting position from it to get the length of the desired output.
Caveats: If there is no parameter (^) after ^14= this will not work. Equally if there is no ^14= (even if the string starts 14=) this will not work. From the information available that's OK; but if this is a concern please say and we can provide something to handle that more complex scenario.
Code to create table & populate demo data
create table myData (someData nvarchar(256))
insert myData (someData)
values ('1=BETA.1.0^2=175^3=812^4=R^5=N^9=1^12=1^13=00032^14=REP NOT FOUND ON REP TABLE, CANNOT INSERT TO REPRGR.^10=107~117~265~1114~3143~3505~3506~3513~5717^11=SA16~1~WY~WY~A~S~20100210~001~SE62^-omitted due to existing Rep Not Found')
, ('1xx^14=something else.^10=xx')
You could try to use a Case When statement with wildcards to find the value that you want.
Example:
SELECT
CASE
WHEN x LIKE '%REP Not Found%'
THEN 'REP NOT FOUND ON REP TABLE, CANNOT INSERT TO REPRGR'
ELSE
''
END AS x
FROM
#T1
You could use this query (assuming MySQL database):
-- item is the column that contains the string
select SUBSTR(item, LOCATE('REP',item), LOCATE('REPRGR.',item) + LENGTH('REPRGR.') - LOCATE('REP', item)) info_msg from Table;
Illustration:
create table parsetest (item varchar(5000));
insert into parsetest values('1=BETA.1.0^2=175^3=812^4=R^5=N^9=1^12=1^13=00032^14=REP NOT FOUND ON REP TABLE, CANNOT INSERT TO REPRGR.^10=107~117~265~1114~3143~3505~3506~3513~5717^11=SA16~1~WY~WY~A~S~20100210~001~SE62^-omitted due to existing Rep Not Found');
select * from parsetest;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| item |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 1=BETA.1.0^2=175^3=812^4=R^5=N^9=1^12=1^13=00032^14=REP NOT FOUND ON REP TABLE, CANNOT INSERT TO REPRGR.^10=107~117~265~1114~3143~3505~3506~3513~5717^11=SA16~1~WY~WY~A~S~20100210~001~SE62^-omitted due to existing Rep Not Found |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
select SUBSTR(item, LOCATE('REP',item), LOCATE('REPRGR.',item) + LENGTH('REPRGR.') - LOCATE('REP', item)) info_msg from parsetest;
+------------------------------------------------------+
| info_msg |
+------------------------------------------------------+
| REP NOT FOUND ON REP TABLE, CANNOT INSERT TO REPRGR. |
+------------------------------------------------------+

Extract number between two substrings in sql

I had a previous question and it got me started but now I'm needing help completing this. Previous question = How to search a string and return only numeric value?
Basically I have a table with one of the columns containing a very long XML string. There's a number I want to extract near the end. A sample of the number would be this...
<SendDocument DocumentID="1234567">true</SendDocument>
So I want to use substrings to find the first part = true so that Im only left with the number.
What Ive tried so far is this:
SELECT SUBSTRING(xml_column, CHARINDEX('>true</SendDocument>', xml_column) - CHARINDEX('<SendDocument',xml_column) +10087,9)
The above gives me the results but its far from being correct. My concern is that, what if the number grows from 7 digits to 8 digits, or 9 or 10?
In the previous question I was helped with this:
SELECT SUBSTRING(cip_msg, CHARINDEX('<SendDocument',cip_msg)+26,7)
and thats how I got started but I wanted to alter so that I could subtract the last portion and just be left with the numbers.
So again, first part of the string that contains the digits, find the two substrings around the digits and remove them and retrieve just the digits no matter the length.
Thank you all
You should be able to setup your SUBSTRING() so that both the starting and ending positions are variable. That way the length of the number itself doesn't matter.
From the sound of it, the starting position you want is right After the "true"
The starting position would be:
CHARINDEX('<SendDocument DocumentID=', xml_column) + 25
((adding 25 because I think CHARINDEX gives you the position at the beginning of the string you are searching for))
Length would be:
CHARINDEX('>true</SendDocument>',xml_column) - CHARINDEX('<SendDocument DocumentID=', xml_column)+25
((Position of the ending text minus the position of the start text))
So, how about something along the lines of:
SELECT SUBSTRING(xml_column, CHARINDEX('<SendDocument DocumentID=', xml_column)+25,(CHARINDEX('>true</SendDocument>',xml_column) - CHARINDEX('<SendDocument DocumentID=', xml_column)+25))
Have you tried working directly with the xml type? Like below:
DECLARE #TempXmlTable TABLE
(XmlElement xml )
INSERT INTO #TempXmlTable
select Convert(xml,'<SendDocument DocumentID="1234567">true</SendDocument>')
SELECT
element.value('./#DocumentID', 'varchar(50)') as DocumentID
FROM
#TempXmlTable CROSS APPLY
XmlElement.nodes('//.') AS DocumentID(element)
WHERE element.value('./#DocumentID', 'varchar(50)') is not null
If you just want to work with this as a string you can do the following:
DECLARE #SearchString varchar(max) = '<SendDocument DocumentID="1234567">true</SendDocument>'
DECLARE #Start int = (select CHARINDEX('DocumentID="',#SearchString)) + 12 -- 12 Character search pattern
DECLARE #End int = (select CHARINDEX('">', #SearchString)) - #Start --Find End Characters and subtract start position
SELECT SUBSTRING(#SearchString,#Start,#End)
Below is the extended version of parsing an XML document string. In the example below, I create a copy of a PLSQL function called INSTR, the MS SQL database does not have this by default. The function will allow me to search strings at a designated starting position. In addition, I'm parsing a sample XML string into a variable temp table into lines and only looking at lines that match my search criteria. This is because there may be many elements with the words DocumentID and I'll want to find all of them. See below:
IF EXISTS (select * from sys.objects where name = 'INSTR' and type = 'FN')
DROP FUNCTION [dbo].[INSTR]
GO
CREATE FUNCTION [dbo].[INSTR] (#String VARCHAR(8000), #SearchStr VARCHAR(255), #Start INT, #Occurrence INT)
RETURNS INT
AS
BEGIN
DECLARE #Found INT = #Occurrence,
#Position INT = #Start;
WHILE 1=1
BEGIN
-- Find the next occurrence
SET #Position = CHARINDEX(#SearchStr, #String, #Position);
-- Nothing found
IF #Position IS NULL OR #Position = 0
RETURN #Position;
-- The required occurrence found
IF #Found = 1
BREAK;
-- Prepare to find another one occurrence
SET #Found = #Found - 1;
SET #Position = #Position + 1;
END
RETURN #Position;
END
GO
--Assuming well formated xml
DECLARE #XmlStringDocument varchar(max) = '<SomeTag Attrib1="5">
<SendDocument DocumentID="1234567">true</SendDocument>
<SendDocument DocumentID="1234568">true</SendDocument>
</SomeTag>'
--Split Lines on this element tag
DECLARE #SplitOn nvarchar(25) = '</SendDocument>'
--Let's hold all lines in Temp variable table
DECLARE #XmlStringLines TABLE
(
Value nvarchar(100)
)
While (Charindex(#SplitOn,#XmlStringDocument)>0)
Begin
Insert Into #XmlStringLines (value)
Select
Value = ltrim(rtrim(Substring(#XmlStringDocument,1,Charindex(#SplitOn,#XmlStringDocument)-1)))
Set #XmlStringDocument = Substring(#XmlStringDocument,Charindex(#SplitOn,#XmlStringDocument)+len(#SplitOn),len(#XmlStringDocument))
End
Insert Into #XmlStringLines (Value)
Select Value = ltrim(rtrim(#XmlStringDocument))
--Now we have a table with multple lines find all Document IDs
SELECT
StartPosition = CHARINDEX('DocumentID="',Value) + 12,
--Now lets use the INSTR function to find the first instance of '">' after our search string
EndPosition = dbo.INSTR(Value,'">',( CHARINDEX('DocumentID="',Value)) + 12,1),
--Now that we know the start and end lets use substring
Value = SUBSTRING(value,(
-- Start Position
CHARINDEX('DocumentID="',Value)) + 12,
--End Position Minus Start Position
dbo.INSTR(Value,'">',( CHARINDEX('DocumentID="',Value)) + 12,1) - (CHARINDEX('DocumentID="',Value) + 12))
FROM
#XmlStringLines
WHERE Value like '%DocumentID%' --Only care about lines with a document id

how to sum up value within one cell SQL

I have some binary values such as 00, 0000, 001000.11111000, 1111100000
I need to sum it up so it turns into 0, 0, 1, 5, 5 ( sum 0s and 1s up)
how can we do that in SQL please?
Thanks
Assumption:
The binary values are stored as string.
Each value is in its own cell in a table. Something like:
BinaryValues (Consider it a column name)
00
0000
001000
and so on.
You want to add up the individual digits to get the sum.
SQL Product you are usind supports functions, looping, string manipulation like substring, extracting string length etc.
As per my best knowledge these are primitives available in all SQL products.
Solution:
Write a function (call it by any name. Ex: AddBinaryDigits) which will take the binary value in string format as input.
Inside the function and do a string manipulation. Extract each digit and add it up. Return the sum as result.
Call the function:
If using binary values stored in a table:
SELECT AddBinaryDigits(BinaryValues) FROM <WhatEverTableName>
If using fixed value:
SELECT AddBinaryDigits('00')
SELECT AddBinaryDigits('0000')
SELECT AddBinaryDigits('001000')
and so on.
Edited to include the request to create function.
CREATE FUNCTION <funtionName>
(
#ParameterName AS VARCHAR(expected string length like 10/15/20 etc.)
)
RETURNS INT
BEGIN
SQL Code to sum
RETURN SummedUpValue
END
Use the below query. If needed convert it into function.
create function dbo.fnSumChars(#someInt VARCHAR(20))
RETURNS INT
AS
BEGIN
DECLARE #count INT = LEN(#someInt),
#counter INT = 1
DECLARE #Sum INT = 0
WHILE #counter <= #count
BEGIN
SELECT #sum += CAST(SUBSTRING(CAST(#someInt AS VARCHAR), #counter, 1) AS int)
SELECT #counter += 1
END
RETURN #sum --5
END
This is the function and you can call this function like below
SELECT dbo.fnSumChars('1111100000')
If these are already in string format, this is the easiest:
select len(replace('1111100000', '0', ''))
No need for a function either, because it can be inlined in the query. Functions, even the light ones, incure perf penalty.

Converting data for whole table SQL

I'm newbie in SQL and have some questions:
How can I convert columns (in my table with more than 10 000 rows) in my SQL table (I'm using SQL Server 2008):
First column is nvarchar (50) and containing different string values, for e.g. like 20131211142319 and it's a date and time - 2013/12/11 14:23:19. How can I convert this value into date & time and affect this on all rows in this column (more than 10 000).
And also I have column with numbers, all this numbers start from # + number - e.g. #8339274. How can I delete character "#" before all numbers in all rows? Note, that numbers in this column have a different length, from 5 characters to 15 characters.
Thank you in advance.
I couldn't find a more elegant solution for the datetime conversion but here you go:
1. DATETIME conversion
This assumes your value is always in the same format you specified:
Example code for you to run
DECLARE #Value VARCHAR(255) = '20131211142319'
SELECT CONVERT(DATETIME,LEFT(#Value,8) + SPACE(1) + STUFF(STUFF(STUFF(RIGHT(#Value,6), 1, 0, REPLICATE('0', 0)),3,0,':'),6,0,':'))
This splits the field into two sections, the DATE portion LEFT(#Value,8) and then the TIME
STUFF(STUFF(STUFF(RIGHT(#Value,6), 1, 0, REPLICATE('0', 0)),3,0,':'),6,0,':'). The TIME portion is essentially just adding in the colon where applicable (see STUFF on MSDN)so that it returns a value such as:
20131211 14:23:19 This makes it applicable to directly convert to a DATETIME.
2. Removing the # from the numbers
Example code for you to run
DECLARE #ValueNumber VARCHAR(255) = '#8339274'
SELECT SUBSTRING(#ValueNumber,2,LEN(#ValueNumber))
The above statement will take your number and only return data from the 2nd value onwards, excluding the #. See SUBSTRING on MSDN
To make this run on your table, just replace my variable names in the SELECT statement with your column names.
Example using the above on columns in a table:
SELECT CONVERT(DATETIME,LEFT([DATECOLUMNNAME],8) +
SPACE(1) + STUFF(STUFF(STUFF(RIGHT([DATECOLUMNNAME],6),
1, 0,REPLICATE('0',0)),3,0,':'),6,0,':')) AS [Date],
SUBSTRING([NUMBERCOLUMNNAME],2,LEN([NUMBERCOLUMNNAME])) AS [Number]
FROM [TABLENAME]
Replace [DATECOLUMNNAME] with the name of the column that holds your datetime value. Replace the [NUMBERCOLUMNNAME] with the name of the column that holds your number with the #.
Then finally replace [TABLENAME] with your table name that contains those columns.
try this : below answer is also correct
declare #a nvarchar(50)
set #a='20131211142319'
select cast(left(#a,4)+'/'+substring(#a,5,2)+'/'+substring(#a,7,2)+ ' '+ substring(#a,9,2)+':'+substring(#a,11,2)+':' +right(#a,2) as datetime)
output's this --2013-12-11 14:23:19.000
declare #a nvarchar(10)
set #a='#1234567'
select replace(#a,'#','')
outputs this--1234567

SQL Between Begins With

I'm attempting to use an alpha between in SQL; however, I want it to be based on the beginnings of the words only. I am using T-SQL on Microsoft SQL Server 2005.
My current SQL is:
SELECT * FROM table WHERE LEFT(word,1) BETWEEN 'a' AND 't
However, this only works for first letter. I'd like to expand this to work for any beginnings of words. For instance, between 'a' and 'ter'.
Now, I am building this dynamically, so I could do:
SELECT * FROM table WHERE LEFT(word,1) >= 'a' AND LEFT(word,3) <= 'ter'
However, I'd like to know if there is a simpler way in SQL to make a dynamic beginning-of-word between.
EDIT:::
Follow up question, words less than the length of the checked value should be considered less than in the between. For instance, me is less than mem so word < 'mem' should include me.
EDIT:::
Attempting using padding, as suggested. The below does work; however, the added 'a's can cause issue. For instance, if we want words between 'a' and 'mera' and the word being checked is 'mer', this will be included because the left trim of 'mer' becomes 'mera' with added characters. I would like a solution that does not include this issue.
DECLARE #lb varchar(50)
DECLARE #ub varchar(50)
SET #lb='ly'
SET #ub='z'
SELECT name
FROM table
WHERE
LEFT(
CASE
WHEN LEN(name) < LEN(#lb) THEN name+REPLICATE('a',LEN(#lb)-LEN(name))
ELSE name
END,
LEN(#lb)
) >= #lb
AND
LEFT(CASE
WHEN LEN(name) < LEN(#ub) THEN name+REPLICATE('a',LEN(#ub)-LEN(name))
ELSE name
END,
LEN(#ub)
) <= #ub
EDIT:::
Attempted solution, although CASE heavy. Mack's solution is better, though this works as well. LEFT('andy', 200000) will return 'andy', not an error as an OO language would, behavior I did not expect.
DECLARE #lb varchar(50)
DECLARE #ub varchar(50)
SET #lb='a'
SET #ub='lyar'
SELECT *
FROM testtable
WHERE
CASE
WHEN LEN(word) < LEN(#lb) THEN 0
WHEN LEFT(word, LEN(#lb)) >= #lb THEN 1
ELSE 0
END = 1
AND
CASE
WHEN LEN(word) < LEN(#ub) THEN
CASE
WHEN LEFT(#ub,LEN(word)) = word THEN 1
ELSE 0
END
WHEN LEFT(word, LEN(#ub)) <= #ub THEN 1
ELSE 0
END = 1
Thanks in advance!
This should work:
SELECT * FROM table WHERE LEFT(word,3) BETWEEN 'a' AND 'ter'
There's no reason why BETWEEN shouldn't be able to compare your three-letter data string to the one-letter 'a'. Any 'axx' will be "greater than" just 'a' by itself, and so will be included.
You need to use the STUFF function to achieve what you are looking for explicitly.
If you follow the link says it deletes a specified number of characters at the end of the string and replaces them with another string. Combine the with the LEN function and we can get you on the road.
--Test Data
DECLARE #table AS TABLE (word char(10))
INSERT INTO #table VALUES ('me')
INSERT INTO #table VALUES ('mem')
INSERT INTO #table VALUES ('tap')
INSERT INTO #table VALUES ('t')
DECLARE #minword char(5)
DECLARE #maxword char(5)
SET #minword='ai'
SET #maxword='t'
--SET #maxword='tb'--(remove the rem at the start of this line to unlock an extra test for comparison...)
--Query
SELECT word
FROM #table
WHERE STUFF(word, LEN(word)+1, 5, 'aaaaa') BETWEEN STUFF(#minword, LEN(#minword)+1, 5, 'aaaaa')
AND STUFF(#maxword, LEN(#maxword)+1, 5, 'aaaaa')
Alternative solution based on your revised requirements:
DECLARE #testtable AS TABLE (word varchar(20))
INSERT INTO #testtable VALUES ('ly')
INSERT INTO #testtable VALUES ('Ly')
INSERT INTO #testtable VALUES ('Zoo')
INSERT INTO #testtable VALUES ('r')
INSERT INTO #testtable VALUES ('traci')
DECLARE #minword varchar(20)
DECLARE #maxword varchar(20)
SET #minword='ly'
SET #maxword='zol'
SELECT word, LEFT(word,LEN(#minword)), LEFT(word,LEN(#maxword)), #minword, #maxword
FROM #testtable
WHERE LEFT(word,LEN(#minword))>=#minword
AND LEFT(word,LEN(#maxword))<=#maxword
If I understand you right you are trying to make this into a proc. If so, what you have will work in a proc with very little change. Something like the following (untested)...
CREATE PROC myProc(#low varchar(30), #high varchar(30)) AS
SELECT * FROM table WHERE
(LEN(word) >= LEN(#low)
AND
(LEN(word) >= LEN(#high)
AND
(LEFT(word, LEN(#low)) >= #low)
AND
LEFT(word, LEN(#high)) <= #high
There are additional conditions to exclude records when 'word' is shorter than either of your parameters. Otherwise, you will get errors on the LEFT function. This may not be 100% but it should get you close.
I believe I've found a working solution. I'm not sure of the speed sacrifices here, but the DB will remain small, so it's a non-issue in my specific case.
I am using C# to build my SQL string with parameters. #lb is the lower bound word-part. #rb is the upper bound word-part. The where clause is inclusive, but could easily be change to exclusive as needed.
SELECT * FROM table
WHERE
CASE
WHEN LEN(word) < LEN(#lb) THEN 0
WHEN LEFT(word, LEN(#lb)) >= #lb THEN 1
ELSE 0
END = 1
AND
CASE
WHEN LEN(word) < LEN(#rb) THEN 1
WHEN LEFT(word, LEN(#rb)) <= #rb THEN 1
ELSE 0
END = 1