Selecting Multiple Substrings from a Field - sql

I'm trying to create a SQL query that selects pieces of a record from a field. Here is a shorten example of what is in one unedited field:
<Name>Example1</Name>
<Type>String</Type>
<Nullable>False</Nullable>
<AllowBlank>False</AllowBlank>
<Prompt>Start Date (DD-MMM-YYYY)</Prompt>
<PromptUser>True</PromptUser> </Parameter>
<Parameter>
<Name>Example2</Name>
<Type>String</Type>
<Nullable>False</Nullable>
<AllowBlank>False</AllowBlank>
<Prompt>Case (Enter Case Number, % for all, OR %AL% for Alberta)</Prompt>
<PromptUser>True</PromptUser>
<DefaultValues>
<Value>%al%</Value>
</DefaultValues>
<Values>
<Value>%al%</Value>
</Values> </Parameter>
<Parameter>
A utter messy right, well I'm trying pull out all names, prompts and if it has a value then its value and put all of that into one field formated. For example the above field should look like this
Name: Example1
Prompt: Start Date (DD-MMM-YYYY)
Name: Example2
Prompt: Case (Enter Case Number, % for all, OR %AL% for Alberta)
Value: %al%
I've tried using STUFF but there can be any number Names with Prompts and values in a single field. My next thought was to use replace to replace all the <> and but that would leave me with the stuff inbetween like so
Name: Example1
String
False
False
Prompt: Start Date (DD-MMM-YYYY)
Name: Example2
String
False
False
Prompt: Case (Enter Case Number, % for all, OR %AL% for Alberta)
True
Value: %al%
%al%
Edit: Another idea that might solve the problem is if I can use REPLACE to replace the unknown length string between or along with the two known characters/strings for example replacing <Type>###</Type> where ### represents any number of characters inbetween the two known strings and . The problem is that I don't know if this is even possible or how to do it if it is.
Any suggestions are apperciated.

so I checked the code with management studio and discover a few errors.
declare #var nvarchar(max)
declare #tag nvarchar(max)
declare #label nvarchar(max)
declare #start int
declare #stop int
declare #len int
declare #needed int
set #var = '<Name>Example1</Name>
<Type>String</Type>
<Nullable>False</Nullable>
<AllowBlank>False</AllowBlank>
<Prompt>Start Date (DD-MMM-YYYY)</Prompt>
<PromptUser>True</PromptUser>
<Parameter> </Parameter>
<Name>Example2</Name>
<Type>String</Type>
<Nullable>False</Nullable>
<AllowBlank>False</AllowBlank>
<Prompt>Case (Enter Case Number, % for all, OR %AL% for Alberta)</Prompt>
<PromptUser>True</PromptUser>
<DefaultValues>
<Value>%al%</Value>
</DefaultValues>
<Values>
<Value>%al%</Value>
</Values>
<Parameter></Parameter>'
set #needed = 0
set #start = charindex('<',#var)
set #stop = charindex('>',#var)
set #len = #stop - #start +1
set #tag = substring(#var,#start,#len)
set #label = substring(#var,#start+1,#len-2)
set #var = replace(#var,#tag,#label + ' : ')
while(#start <> 0)
begin
set #start = charindex('<',#var)
set #stop = charindex('>',#var)
set #len = #stop - #start +1
if(#start <> 0)
begin
set #tag = substring(#var,#start,#len)
if(charindex('/',#tag) = 0)
begin
set #label = substring(#var,#start+1,#len-2)+ ' : '
if(lower(#label) <> 'name : ' and lower(#label) <> 'value : ' and lower(#label) <> 'prompt : ')
begin
set #needed = 0
set #var = replace(#var,#tag,'')
set #start = #stop - len(#tag)
set #stop = charindex('<',#var)
set #len = #stop - #start
set #tag = substring(#var,#start,#len)
set #var = replace(#var,#tag,'')
end
end
else
begin
set #label = ''
end
set #var = replace(#var,#tag,#label)
end
end
print replace(#var,'
','')
and this results in:
Name : Example1
Prompt : Start Date (DD-MMM-YYYY)
Name : Example2
Prompt : Case (Enter Case Number, % for all, OR %AL% for Alberta) Value :
%al%

this is what I came up with. I hope it helps. basically it looks for the tags, if it's a closing tag it removes it and if it's an open tag it is replaces by the label variant.
declare #var nvarchar(max)
declare #tag nvarchar(max)
declare #label nvarchar(max)
declare #start int
declare #stop int
declare #len int
set #var = '<Name>Example1</Name>
<Type>String</Type>
<Nullable>False</Nullable>
<AllowBlank>False</AllowBlank>
<Prompt>Start Date (DD-MMM-YYYY)</Prompt>
<PromptUser>True</PromptUser>
<Parameter> </Parameter>
<Name>Example2</Name>
<Type>String</Type>
<Nullable>False</Nullable>
<AllowBlank>False</AllowBlank>
<Prompt>Case (Enter Case Number, % for all, OR %AL% for Alberta)</Prompt>
<PromptUser>True</PromptUser>
<DefaultValues>
<Value>%al%</Value>
</DefaultValues>
<Values>
<Value>%al%</Value>
</Values>
<Parameter></Parameter>'
set #start = charindex('<',#var)
set #stop = charindex('>',#var)
set #len = #stop - #start +1
set #tag = substring(#var,#start,#len)
set #label = substring(#var,#start+1,#len-2)
set #var = replace(#var,#tag,#label + ' : ')
while(#start <> 0)
begin
set #start = charindex('<',#var)
set #stop = charindex('>',#var)
set #len = #stop - #start +1
if(#start <> 0)
begin
set #tag = substring(#var,#start,#len)
if(charindex('/',#tag) = 0)
begin
set #label = substring(#var,#start+1,#len-2)+ ' : '
end
else
begin
set #label = ''
end
set #var = replace(#var,#tag,#label)
end
end
print #var
this results in:
Name : Example1
Type : String
Nullable : False
AllowBlank : False
Prompt : Start Date (DD-MMM-YYYY)
PromptUser : True
Parameter :
Name : Example2
Type : String
Nullable : False
AllowBlank : False
Prompt : Case (Enter Case Number, % for all, OR %AL% for Alberta)
PromptUser : True
DefaultValues :
Value : %al%
Values :
Value : %al%
Parameter :

I changed the code so that only the name, prompt and value parts will be saved.
A little warning though I changed the code in notepad++ so it could have a bug.
the idea is that you put this code in a function and pass the #var part as a parameter.
declare #var nvarchar(max)
declare #tag nvarchar(max)
declare #label nvarchar(max)
declare #start int
declare #stop int
declare #len int
declare #needed int
set #var = '<Name>Example1</Name>
<Type>String</Type>
<Nullable>False</Nullable>
<AllowBlank>False</AllowBlank>
<Prompt>Start Date (DD-MMM-YYYY)</Prompt>
<PromptUser>True</PromptUser>
<Parameter> </Parameter>
<Name>Example2</Name>
<Type>String</Type>
<Nullable>False</Nullable>
<AllowBlank>False</AllowBlank>
<Prompt>Case (Enter Case Number, % for all, OR %AL% for Alberta)</Prompt>
<PromptUser>True</PromptUser>
<DefaultValues>
<Value>%al%</Value>
</DefaultValues>
<Values>
<Value>%al%</Value>
</Values>
<Parameter></Parameter>'
set #start = charindex('<',#var)
set #stop = charindex('>',#var)
set #len = #stop - #start +1
set #tag = substring(#var,#start,#len)
set #label = substring(#var,#start+1,#len-2)
set #var = replace(#var,#tag,#label + ' : ')
while(#start <> 0)
begin
set #start = charindex('<',#var)
set #stop = charindex('>',#var)
set #len = #stop - #start +1
if(#start <> 0)
begin
set #tag = substring(#var,#start,#len)
if(charindex('/',#tag) = 0)
begin
set #label = substring(#var,#start+1,#len-2)+ ' : '
if(lower(#label) = 'name' or lower(#label) = 'value' or lower(#label) = 'prompt')
begin
set #needed = 1
end
else
begin
set #needed = 0
end
end
else
begin
set #label = ''
end
if(#needed = 1)
begin
set #var = replace(#var,#tag,#label)
end
else
begin
set #var = replace(#var,#tag,'')
set #start = #stop
set #stop = charindex('<',#var)
set #tag = substring(#var,#start,#stop)
set #var = replace(#var,#tag,'')
end
end
end
print #var

Related

How to convert syntax from Foxpro to SQL Server

LOCAL in_txt
in_txt='mohammad'
txt_len=LEN(in_txt)
rev_txt=''
FOR ii=1 TO txt_len
w_chr=SUBSTR(in_txt,ii,1)
w_asc=IIF(ASC(w_chr)=32,32,ASC(w_chr)+1)
* ?' #'+w_chr+'='+CHR(w_asc)+'*'+STR(ASC(w_chr))+'>'+STR(ASC(w_chr)+1)+'*'+CHR(ASC(w_chr))+'>'+CHR(ASC(w_chr)+1)
rev_txt=rev_txt+ CHR(w_asc)
ENDFOR
return rev_txt
I understand solve my problem * character is using comment in foxpro.
Here is the equivalent SQL
DECLARE #in_txt varchar(100)
DECLARE #txt_Len int
DECLARE #rev_txt varchar(100)=''
DECLARE #i int=0
DECLARE #w_chr char(1)
DECLARE #w_asc int
SET #in_txt='mohammad'
SET #txt_len=LEN(#in_txt)
WHILE #i < #txt_len
BEGIN
SET #i = #i + 1
SET #w_chr=SUBSTRING(#in_txt,#i,1)
SET #w_asc = ASCII(#w_chr)
IF #w_asc <> 32 SET #w_asc=#w_asc+1
PRINT '#'+#w_chr+'='+CHAR(#w_asc)+'*'+STR(ASCII(#w_chr))+'>'+STR(ASCII(#w_chr)+1)+'*'+CHAR(ASCII(#w_chr))+'>'+CHAR(ASCII(#w_chr)+1)
SET #rev_txt = #rev_txt + CHAR(#w_asc)
END
PRINT #rev_Txt
It appears to be a very simple encryption of the text. The Print statement in the loop (which is the equivalent of the ? in Foxpro) is the debugger statement
It is a badly written piece of VFP code. * marks the line as a comment and basically it is replacing all the characters in the string except the char 32 (SPACE) with the next in ASCII chart. That is not something you could easily convert in MS SQL though, because in VFP a string could have any character in it including char(0). Assuming you don't have char 0:
Declare #in_txt varbinary(MAX);
DECLARE #rev_txt varbinary(MAX);
set #in_txt = CAST('mohammad' AS VARBINARY(MAX));
declare #i int;
declare #w_char int;
set #i = 1;
SET #rev_txt = CAST('' AS VARBINARY(MAX));
WHILE #i <= Len(#in_txt)
Begin
set #w_char = ASCII(SUBSTRING(#in_txt,#i,1));
SET #rev_txt = #rev_txt +
CAST(#w_char + case when #w_char = 32 then 0 else 1 end AS BINARY(1));
SET #i = #i +1;
END
SELECT #rev_txt;

Function to check the input number of words

Need help to create a function that returns TRUE or FALSE. TRUE - if type 1 or 3 words (like '__hello_', '_hello', '_hello my frend' - spaces should be cut), if the condition is not fulfilled FALSE
CREATE FUNCTION dbo.nazvFac(#f nvarchar(30))
RETURNS bit
AS
BEGIN
DECLARE #l int = 1, #s nvarchar(30), #i int = 0, #b bit
WHILE LTRIM(RTRIM(LEN(#f))) >= #l --error here, but I do not know how to fix it
BEGIN
SET #s = SUBSTRING(#f, #l, 1)
IF #s BETWEEN 'А' AND 'я'
SET #l += 1
ELSE IF #s = ' '
BEGIN
SET #l -= 1
SET #s = SUBSTRING(#f, #l, 1)
SET #s = RTRIM(#s)
SET #l += 2
SET #i += 1
END
ELSE
BREAK
END
IF #i = 0 OR #i = 2
SET #b = 'TRUE'
ELSE
SET #b = 'FALSE'
RETURN #b
END
GO
WHILE LTRIM(RTRIM(LEN(#f))) >= #l --error here, but I do not know how to fix it
LEN() returns an int, which you are then passing to a string function: RTRIM().
You want to return TRUE only if there are one or three words? This should do it:
CREATE FUNCTION dbo.nazvFac(#f NVARCHAR(30))
RETURNS BIT
AS
BEGIN
DECLARE #l INT, #b BIT
SET #l = LEN(#f) - LEN(REPLACE(#f, ' ', '')) + 1
IF #l == 1 OR #l == 3
SET #b = 'TRUE'
ELSE
SET #b = 'FALSE'
RETURN #b
END
Also, JC. is right about the len() error.
You should trim the string and then check the length.
CREATE FUNCTION dbo.nazvFac(#f NVARCHAR(30))
RETURNS BIT
AS
BEGIN
DECLARE #l INT = 1, #s NVARCHAR(30), #i INT = 0, #b BIT
WHILE LEN(LTRIM(RTRIM(#f))) >= #l --I think youn need to trim the string and then check length
BEGIN
SET #s = SUBSTRING(#f, #l, 1)
IF #s BETWEEN 'А' AND 'я'
SET #l += 1
ELSE IF #s = ' '
BEGIN
SET #l -= 1
SET #s = SUBSTRING(#f, #l, 1)
SET #s = RTRIM(#s)
SET #l += 2
SET #i += 1
END
ELSE
BREAK
END
IF #i = 0 OR #i = 2
SET #b = 'TRUE'
ELSE
SET #b = 'FALSE'
RETURN #b
END
GO

SQL Server: how to declare and set variable in dynamic procedure

I would like to declare and set a variable as part of a dynamic procedure.
I am new to this so the following is just to indicate what I am trying to achieve.
Can someone show me how to write this correctly (just regarding these lines) ?
#searchMain nvarchar(100) = '',
#searchInput nvarchar(256) = ''
AS
BEGIN
SET NOCOUNT ON;
BEGIN
DECLARE #sql nvarchar(max),
#searchDate datetime
CASE WHEN #searchMain = 'col1' THEN SET #searchDate = #searchInput ELSE SET #searchDate = '' END
SET #sql = 'SELECT TOP 100
-- ...
Many thanks in advance for any help with this, Mike.
Change this:
CASE WHEN #searchMain = 'col1' THEN SET #searchDate = #searchInput ELSE SET #searchDate = '' END
To this:
SET #searchDate = CASE WHEN #searchMain = 'col1' THEN #searchInput ELSE '' END

Invalid length parameter passed to the LEFT or SUBSTRING function

Following is my query and it works fine when I have like TRAILER_MAKE_MODEL 'testing ~ testing, test ~ testq,' ends with comma but can't handle 'testing ~ testing, test ~ testq' same as for other variable TRAILER_IDV. I tried my best but cant work it out any help would be appreciated.
My aim is to get the comma separated value for xml.
DECLARE #TRAILER_MAKE_MODEL VARCHAR(MAX)
DECLARE #TRAILER_IDV VARCHAR(MAX)
DECLARE #USER_TYPE VARCHAR(MAX)
DECLARE #START_INDEX_1 INT
DECLARE #START_INDEX_4 INT
DECLARE #END_INDEX_1 INT
DECLARE #END_INDEX_4 INT
DECLARE #VALUE VARCHAR(50)
DECLARE #QUERY VARCHAR(MAX)
set #TRAILER_MAKE_MODEL='testing ~ testing, test ~ testq,'
set #TRAILER_IDV='3500, 3400,'
set #USER_TYPE='MOBILE'
set #QUERY = ''
set #START_INDEX_1 = 1
set #START_INDEX_4 = 1
set #END_INDEX_1 = 0
if ISNULL(#TRAILER_MAKE_MODEL,'') <> ''
begin
WHILE #START_INDEX_1 > 0 and #START_INDEX_1 < len(#TRAILER_MAKE_MODEL)
BEGIN
SET #END_INDEX_1 = CHARINDEX(',',#TRAILER_MAKE_MODEL,#START_INDEX_1)
if #END_INDEX_1 = 0 and #START_INDEX_1 = 1
Begin
SET #END_INDEX_1 = len(#TRAILER_MAKE_MODEL)
END
if #USER_TYPE <> 'MOBILE'
Begin
SET #END_INDEX_1 = #END_INDEX_1 +1
End
SET #VALUE = SUBSTRING(#TRAILER_MAKE_MODEL,#START_INDEX_1,#END_INDEX_1 - #START_INDEX_1)
SET #QUERY = #QUERY + 'UNION ALL SELECT ''' + #VALUE + ''' TRAILER_MAKE_MODEL'
SET #END_INDEX_4 = CHARINDEX(',',#TRAILER_IDV,#START_INDEX_4)
if #END_INDEX_4 = 0 and #START_INDEX_4 = 1
Begin
SET #END_INDEX_4 = len(#TRAILER_IDV)
END
if #USER_TYPE <> 'MOBILE'
Begin
SET #END_INDEX_4 = #END_INDEX_4 +1
End
SET #VALUE = SUBSTRING(#TRAILER_IDV,#START_INDEX_4,#END_INDEX_4 - #START_INDEX_4)
SET #QUERY = #QUERY + ',' + #VALUE + 'TRAILER_IDV '
print #QUERY
SET #START_INDEX_1 = #END_INDEX_1 + 1
SET #START_INDEX_4 = #END_INDEX_4 + 1
END
select #QUERY=substring(#QUERY, 10, LEN(#QUERY) - 9)
EXEC (#QUERY)
END
You already have a lot of code here so two extra lines where you assign a comma at the end of each string should probably not slow things down for you or make the code less maintainable.
SET #TRAILER_MAKE_MODEL += ',';
SET #TRAILER_IDV += ',';
I don't really understand what your code does but to get the result you are getting you can use a split string function that returns the index of the item like this.
select T1.Item as TRAILER_MAKE_MODEL,
T2.Item as TRAILER_IDV
from dbo.SplitString(#TRAILER_MAKE_MODEL, ',') as T1
inner join dbo.SplitString(#TRAILER_IDV, ',') as T2
on T1.ItemNumber = T2.ItemNumber

SQL Server 2005:charindex starting from the end

I have a string 'some.file.name',I want to grab 'some.file'.
To do that,I need to find the last occurrence of '.' in a string.
My solution is :
declare #someStr varchar(20)
declare #reversedStr varchar(20)
declare #index int
set #someStr = '001.002.003'
set #reversedStr = reverse(#someStr)
set #index = len(#someStr) - charindex('.',#reversedStr)
select left(#someStr,#index)
Well,isn't it too complicated?I was just intented to using 'some.file' in a where-clause.
Anyone has a good idea?
What do you need to do with it?? Do you need to grab the characters after the last occurence of a given delimiter?
If so: reverse the string and search using the normal CHARINDEX:
declare #test varchar(100)
set #test = 'some.file.name'
declare #reversed varchar(100)
set #reversed = REVERSE(#test)
select
REVERSE(SUBSTRING(#reversed, CHARINDEX('.', #reversed)+1, 100))
You'll get back "some.file" - the characters up to the last "." in the original file name.
There's no "LASTCHARINDEX" or anything like that in SQL Server directly. What you might consider doing in SQL Server 2005 and up is great a .NET extension library and deploy it as an assembly into SQL Server - T-SQL is not very strong with string manipulation, whereas .NET really is.
A very simple way is:
SELECT
RIGHT(#str, CHARINDEX('.', REVERSE(#str)) - 1)
This will also work:
DECLARE
#test VARCHAR(100)
SET #test = 'some.file.name'
SELECT
LEFT(#test, LEN(#test) - CHARINDEX('.', REVERSE(#test)))
Take one ')'
declare #test varchar(100)
set #test = 'some.file.name'
select left(#test,charindex('.',#test)+charindex('.',#test)-1)
CREATE FUNCTION [dbo].[Instr] (
-------------------------------------------------------------------------------------------------
-- Name: [dbo].[Instr]
-- Purpose: Find The Nth Value Within A String
-------------------------------------------------------------------------------------------------
-- Revisions:
-- 25-FEB-2011 - HESSR - Initial Revision
-------------------------------------------------------------------------------------------------
-- Parameters:
-- 1) #in_FindString - NVARCHAR(MAX) - INPUT - Input Find String
-- 2) #in_String - NVARCHAR(MAX) - INPUT - Input String
-- 3) #in_StartPos - SMALLINT - INPUT - Position In The String To Start Looking From
-- (If Start Position Is Negative, Search Begins At The End Of The String)
-- (Negative 1 Starts At End Position 1, Negative 3 Starts At End Position Minus 2)
-- 4) #in_Nth - SMALLINT - INPUT - Nth Occurrence To Find The Location For
-------------------------------------------------------------------------------------------------
-- Returns: SMALLINT - Position Of String Segment (Not Found = 0)
-------------------------------------------------------------------------------------------------
#in_FindString NVARCHAR(MAX),
#in_String NVARCHAR(MAX),
#in_StartPos SMALLINT = NULL,
#in_Nth SMALLINT = NULL
)
RETURNS SMALLINT
AS
BEGIN
DECLARE #loc_FindString NVARCHAR(MAX);
DECLARE #loc_String NVARCHAR(MAX);
DECLARE #loc_Position SMALLINT;
DECLARE #loc_StartPos SMALLINT;
DECLARE #loc_Nth SMALLINT;
DECLARE #loc_Idx SMALLINT;
DECLARE #loc_FindLength SMALLINT;
DECLARE #loc_Length SMALLINT;
SET #loc_FindString = #in_FindString;
SET #loc_String = #in_String;
SET #loc_Nth = ISNULL(ABS(#in_Nth), 1);
SET #loc_FindLength = LEN(#loc_FindString+N'.') - 1;
SET #loc_Length = LEN(#loc_String+N'.') - 1;
SET #loc_StartPos = ISNULL(#in_StartPos, 1);
SET #loc_Idx = 0;
IF (#loc_StartPos = ABS(#loc_StartPos))
BEGIN
WHILE (#loc_Idx < #loc_Nth)
BEGIN
SET #loc_Position = CHARINDEX(#loc_FindString,#loc_String,#loc_StartPos);
IF (#loc_Position > 0)
SET #loc_StartPos = #loc_Position + #loc_FindLength
ELSE
SET #loc_Idx = #loc_Nth;
SET #loc_Idx = #loc_Idx + 1;
END;
END
ELSE
BEGIN
SET #loc_StartPos = ABS(#loc_StartPos);
SET #loc_FindString = REVERSE(#in_FindString);
SET #loc_String = REVERSE(#in_String);
WHILE (#loc_Idx < #loc_Nth)
BEGIN
SET #loc_Position = CHARINDEX(#loc_FindString,#loc_String,#loc_StartPos);
IF (#loc_Position > 0)
SET #loc_StartPos = #loc_Position + #loc_FindLength
ELSE
SET #loc_Idx = #loc_Nth;
SET #loc_Idx = #loc_Idx + 1;
END;
IF (#loc_Position > 0)
SET #loc_Position = #loc_Length - #loc_Position + (1 - #loc_FindLength) + 1;
END;
RETURN (#loc_Position);
END;
GO
Here is a shorter version
DECLARE #someStr varchar(20)
set #someStr = '001.002.003'
SELECT REVERSE(Substring(REVERSE(#someStr),CHARINDEX('.', REVERSE(#someStr))+1,20))