Related
I am not sure why this is happening, but when I run some code that takes a long multi-value sub-value string, I'm getting a random error. The error is supposedly that I'm trying to do a insert into a temp table that already exists, though I'm not sure how this is possible since inside the top of the first base loop, I'm dropping the tables if they do exist. That way the prior run can use them, them drop and run through again if need be.
Now I'm almost certain another error is failing, then causing a misleading return message but not sure.
Here is the full stored procedure. Also, I'm a stack dev so if you have any good pointers for this query please feel free to make note. Thanks in advance.
DATA string for testing that would possibly come into this procedure:
MITUTOYO~103-217~DESC~ ^BROWN & SHARPE~73~DESC~ ^MITUTOYO~103-188~DESC~ ^MITUTOYO~103-224A~DESC~ ^MITUTOYO~103-225A~DESC~ ^MITUTOYO~103-225A~DESC~ ^MITUTOYO~103-189A~DESC~ ^
For each one of the main multi values within the ^ character, we would look up the Manufacturer and Model and see if it exist. If so return it, if not return it with a new suggestions list of multi-values in the suggestionsList column. Here is the example of one item sent in, then no matches found so does the fuzzy lookup.
Manufacturer ManufacturerPartNumber ManufacturerDescription Price ItemType MfrFound ModelFound Score SuggestionGroup
Analyzer Network, 0.00, NA, 0, 1, 0, STANDARD,,USE FOR HYPRO STANDARD BORE
GAGES,31.00,6, ^ Harvey Wells,,FORCE GAUGE 0 35 GMS,93.50,0, ^
The full stored procedure:
ALTER PROCEDURE DATA_FuzzyAssetMatchLARGE
/*
PROP: #LISTIN - The entire multi file string sent in from the data file of records.
I=Since the records sent in are a multi value record of sub values meaning (v1.Mfr, v1.Model, v1.Description ^ v2.Mfr, v2.Model, v2.Description ^ ....)
we could in theory have thousands of records to n(3) of string length unknown (n). This being said, we set the value to varchar(max).
varchar(max) unlike varchar which holds (8000 characters max about 65k bytes), varchar(max) can hold 2 gigs worth of data. That equates to about
1 billion characters since in .NET and others one CHAR is a 2 byte system. This holds more than enough.
*/
#LISTIN VARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
SET ARITHABORT OFF
SET ANSI_WARNINGS OFF
SET FMTONLY OFF
declare #errorMessage varchar(150) -- ideally would be nice to append to each row so we knew what failed but not break and continue. Not like that at moment
-- ITEMS USED FOR TOP LEVEL RECURSIVE SPLIT LOOP --
declare #pos INT;
declare #len INT;
declare #value varchar(max); -- the value of the first splits (objects) delimed by the ^ character
-- Counter variables used to loop and build the one column Suggestion of all matching items
declare #MaxCount integer;
declare #Count integer;
declare #suggestionsStringBuilder varchar(350); --holds the built list of any suggestions.
declare #objectManufacturer varchar(100);
declare #objectModel varchar(150);
declare #objectDescription varchar(150); --the context for this instance to run the params selects for Mfr, Model, Desc
declare #counterIndex int = 0;
declare #objectString varchar(250);
--USED FOR SECOND LEVEL RECURSIVE SPLIT LOOP
declare #objectPosVal INT;
declare #objectLenVal INT;
declare #objectSubstring varchar(250); -- the value used for the secondary split (each object properties such as the actual Mfr, Model, Description, so on....)
--cursor declarations
DECLARE #suggestionsListCursor CURSOR;
DECLARE #CURSOR_Mfr varchar(150);
DECLARE #CURSOR_Model varchar(150);
DECLARE #CURSOR_Desc varchar(150);
DECLARE #CURSOR_Type varchar(20);
DECLARE #CURSOR_Price MONEY;
DECLARE #CURSOR_Score int;
--TEMP TABLE TO STORE FUZZY PREDFINED TABLES IN
IF OBJECT_ID('BASE') IS NOT NULL DROP TABLE BASE
--CREATE THE TEMPTABLE TO WORK WITH
CREATE TABLE BASE
(
Manufacturer varchar(150) null,
ManufacturerPartNumber Varchar(100) null,
ManufacturerDescription varchar(150) null,
Score int ,
Price money,
ItemType varchar(20),
ModelFound bit not null,
MfrFound bit not null,
SuggestionGroup varchar(MAX) null
)
--THIS IS THE LOOP TO PREPARE THE DELIM STRING AND DO A FUZZY MATH OR STRAIGHT MATCH ON EACH OBJECT ROW
set #pos = 0;
set #len = 0;
while charindex('^', #LISTIN, #pos+1)>0
begin
if object_id('tempdb..#TEMPPMFR') is not null drop table #TEMPPMFR
if object_id('tmpdb..#TEMPPMFRMODEL') is not null drop table #TEMPPMFRMODEL
-- stop if there was an error --
if(#errorMessage is not null and #errorMessage != '')
break;
set #suggestionsStringBuilder = '';
set #len = charindex('^', #LISTIN, #pos+1) - #pos;
set #VALUE = substring(#LISTIN,#POS,#LEN-1);
set #objectLenVal = 0;
set #objectPosVal = 0;
set #counterIndex = 0;
set #suggestionsStringBuilder = ''
set #objectManufacturer = ''
set #objectModel = ''
set #objectDescription = ''
--THE LIST COMING IN IS A MULTI-DEMENSIONAL ARRAY OR MULTI-VALUE. IT IS A GROUP OF ENTITYIES, THEN EACH
--ENTITYT IS A GROUP OF PROPERTIES. OUR OUTTER LOOP WE SPLIT ON ENTITITIES.
-- EXAMPLE: #F, #M, #D = Manufacturer, odel, Description
while charindex('~', #value, #objectPosVal+1)>0
begin
set #objectLenVal = charindex('~', #value, #objectPosVal+1) - #objectPosVal
set #objectSubstring = substring(#value, #objectPosVal, #objectLenVal)
if(#counterIndex=0)
set #objectManufacturer = LTRIM(RTRIM(#objectSubstring))
else if(#counterIndex=1)
set #objectModel = LTRIM(RTRIM(#objectSubstring))
else
begin
set #objectDescription = LTRIM(RTRIM(#objectSubstring))
break
end
set #objectPosVal = charindex('~', #value, #objectPosVal+#objectLenVal) +1
end
-- ****
-- **** WE HAVE THE MANUFACTURER AND THE MODEL SO JUST RETURN THE DATA
-- ****
if((select top 1 1 FROM Products_OurProducts_Products where Manufacturer = #objectManufacturer and ManufacturerPartNumber = #objectModel) > 0)
begin try
insert into BASE (
Manufacturer
,ManufacturerPartNumber
,ManufacturerDescription
,Price,ItemType
,MfrFound
,ModelFound
,Score
,SuggestionGroup
)
select
POP.Manufacturer
,POP.ManufacturerPartNumber as Model
,POP.Description
,CONVERT(money,POP.Price) as Price
,POP.ItemType
,CAST('1' as bit) as MfrFound
,CAST('1' as bit) as ModelFound
,CAST('-1' as int) as Score
,'' as SuggestionGroup
from
Products_OurProducts_Products as POP
where
POP.Manufacturer = #objectManufacturer
and
POP.ManufacturerPartNumber = #objectManufacturer
end try
begin catch
SET #errorMessage = (
select
'Number: ' + CAST(ERROR_NUMBER() as varchar(15)) + ' Message:' + ERROR_MESSAGE() AS ErrorMessage
);
end catch
else
-- ****
-- **** WE EITHER FOUND MANUFACTURER SO FUZZY ON MODEL OR VICE VERSA
-- ****
begin try
if((select top 1 1 from Products_OurProducts_Products where Manufacturer = #objectManufacturer) > 0)
begin
--we have to build these temp tables os dynamic columns exist such as MfrFound, ModelFound
select
PMFR.Manufacturer
,PMFR.ManufacturerPartNumber
,PMFR.Description AS ManufacturerDescription
,convert(money,PMFR.Price) as Price
,PMFR.ItemType
,cast('1' as bit) as MfrFound
,cast('0' as bit) as ModelFound
,'' as SuggestionGroup
into
#TEMPPMFR
from
Products_OurProducts_Products as PMFR
where
PMFR.Manufacturer = #objectManufacturer
set #SuggestionsListCursor = cursor for
select top 5
P.Manufacturer
,P.ManufacturerPartNumber as Model
,P.ManufacturerDescription AS 'Description'
,P.Price
,fms.score as Score
from #TEMPPMFR as P
cross apply (
select
dbo.FuzzyControlMatch(#objectModel, P.ManufacturerPartNumber) AS score
) as fms
where
P.Manufacturer = #objectManufacturer
order by
fms.score
desc
open #SuggestionsListCursor
fetch next from
#SuggestionsListCursor
into
#CURSOR_Mfr
,#CURSOR_Model
,#CURSOR_Desc
,#CURSOR_Price
,#CURSOR_Score
while ##FETCH_STATUS = 0
begin
if #suggestionsStringBuilder!=''
set #suggestionsStringBuilder=#suggestionsStringBuilder + #CURSOR_Mfr + ',' + #CURSOR_Model + ',' + #CURSOR_Desc + ',' + convert(varchar(20),#CURSOR_Price) + ',' + convert(varchar(4),#CURSOR_Score) + ', ^ '
else
set #suggestionsStringBuilder = #CURSOR_Mfr + ',' + #CURSOR_Model + ',' + #CURSOR_Desc + ',' + convert(varchar(20),#CURSOR_Price) + ',' + convert(varchar(4),#CURSOR_Score) + ', ^ '
fetch next from
#SuggestionsListCursor
into
#CURSOR_Mfr
,#CURSOR_Model
,#CURSOR_Desc
,#CURSOR_Price
,#CURSOR_Score
end
--Now we insert the original Mfr, Model, Desc, and the suggestions list we build
insert into BASE values(
#objectManufacturer
,#objectModel
,#objectDescription
,'0'
,'0'
,'NA'
,'1'
,'0'
,#suggestionsStringBuilder
)
close #SuggestionsListCursor
deallocate #SuggestionsListCursor
end
else
--IF HAVE A FUZZY AT MFR, THEN WE NEED TO GRAB BEST CHOICE AND GO DOWN.
--WE COULD HAVE POSIBLY CANDIDATES FOR THIS SO WHEN TO STOP RECURSIVENESS AND SAY ADDING NEW ENTRY?
begin
--AT MOMENT JUST RETURN TOP FOUND MFR THEN SELECT FROM THAT TO SEE RESULT TESTS
--FIRST LETS SEE IF SENT MODEL EXIST AND IF SO, PULL THAT THEN RANK AGAINST MFR FOR IT
if((select top 1 1 from Products_OurProducts_Products where ManufacturerPartNumber = #objectModel) > 0)
begin
select
Manufacturer
,ManufacturerPartNumber
,Description AS ManufacturerDescription
,CONVERT(money,Price) AS Price
,ItemType
,CAST('0' as bit) as MfrFound
,CAST('1' as bit) as ModelFound
,'' as SuggestionGroup
into
#TEMPPMFRMODEL
from
Products_OurProducts_Products
where
ManufacturerPartNumber = #objectModel
set #SuggestionsListCursor = cursor for
select top 5
P.Manufacturer
,P.ManufacturerPartNumber as Model
,P.ManufacturerDescription as 'Description'
,P.Price
,fms.score as Score
from #TEMPPMFRMODEL AS P
CROSS APPLY (
select
dbo.FuzzyControlMatch(#objectManufacturer, P.Manufacturer) AS score
) AS fms
where
P.ManufacturerPartNumber = #objectModel
order by
fms.score
desc
--OPEN CURSOR NOW--
open #SuggestionsListCursor
-- NOW LOOP THE RESULTS AND BUILD DEMLIMETER STRING OF SUGESTIONS FOR MFR--
fetch next from #SuggestionsListCursor into #CURSOR_Mfr, #CURSOR_Model, #CURSOR_Desc, #CURSOR_Price, #CURSOR_Score
while ##FETCH_STATUS = 0
begin
if #suggestionsStringBuilder != ''
set #suggestionsStringBuilder = #suggestionsStringBuilder + #CURSOR_Mfr + ',' + #CURSOR_Model + ',' + #CURSOR_Desc + ',' + convert(varchar(20),#CURSOR_Price) + ',' + convert(varchar(4),#CURSOR_Score) + ', ^ '
else
set #suggestionsStringBuilder = #CURSOR_Mfr + ',' + #CURSOR_Model + ',' + #CURSOR_Desc + ',' + convert(varchar(20),#CURSOR_Price) + ',' + convert(varchar(4),#CURSOR_Score) + ', ^ '
fetch next from #SuggestionsListCursor
into #CURSOR_Mfr, #CURSOR_Model, #CURSOR_Desc, #CURSOR_Price, #CURSOR_Score
end
insert into BASE values(
#objectManufacturer
,#objectModel
,#objectDescription
,'0'
,'0'
,'NA'
,'1'
,'0'
,#suggestionsStringBuilder
)
close #SuggestionsListCursor ;
deallocate #SuggestionsListCursor ;
end
else
insert into BASE values(
#objectManufacturer
,#objectModel
,#objectDescription
,'0'
,'0'
,'NA'
,'0'
, '0'
, ''
)
end
--THEN SEE IF EXIST USING FUZZY FOUND MFR, TO SEEK MODEL NUMBER ETC.
END TRY
BEGIN CATCH
SET #errorMessage = (
select
'Number: ' + CAST(ERROR_NUMBER() as varchar(15)) + ' Message:' + ERROR_MESSAGE() AS ErrorMessage
);
END CATCH
SET #pos = CHARINDEX('^', #LISTIN, #pos+#len) +1
END
if(#errorMessage is not null and #errorMessage != '')
select #errorMessage as 'ErrorInfo'
else
select
Manufacturer
,ManufacturerPartNumber
,ManufacturerDescription
,Price
,ItemType
,MfrFound
,ModelFound
,Score
,SuggestionGroup
from
BASE
--NOW REMOVE THE TEMP TABLE
If(OBJECT_ID('BASE') Is Not Null)
BEGIN
DROP TABLE BASE
END
IF(OBJECT_ID('tempdb..#TEMPPMFR') IS NOT NULL)
BEGIN
DROP TABLE #TEMPPMFR
END
IF(OBJECT_ID('tmpdb..#TEMPPMFRMODEL') IS NOT NULL)
BEGIN
DROP TABLE #TEMPPMFRMODEL
END
END
GO
I'm facing some problem with a SQL query, I'm creating a variable :
declare #material varchar(500)
declare #typ varchar(200)
declare #strsql varchar(max)
set #typ = 'papier'
select #material = (SELECT + rtrim(ltrim([grupa])) + ','
FROM [test].[dbo].[GT]
WHERE (typ = #typ) FOR XML PATH(''))
set #material = left(#material, len(#material)-1)
set #material = replace(#material, ',' ,''',''')
set #material = '''' + #material + ''''
select #material
The output from variable is :
'test','test2'
And here is a little part of my main code :
SELECT
Number,
isnull(sum((case
when [Group] in ('test','test2')
then isnull(cast([Quantity] as int), 0)
end)), 0) as other
FROM
[dbo].[test-table]
which works correct but when I'm trying to do this like that :
SELECT
Number,
isnull(sum((case
when [Group] in (#material)
then isnull(cast([Quantity] as int), 0)
end)), 0) as other
FROM
[dbo].[test-table]
Output is wrong (different). Can anyone tell me why? It's kinda this same.
It's kind of the same, but it's not the same. What you are looking to get is: in ('test','test2'), what you get is: in (''test','test2'').
You will have to build the rest of your query dynamically, something like:
DECLARE #SQL NVARCHAR(MAX) = 'SELECT
Number
,isnull(sum((case when [Group] in ('+ #material + ')
then isnull(cast([Quantity] as int),0) end)),0) as other
from [dbo].[test-table]'
EXEC sys.sp_executesql #SQL
If you say the output is wrong, it's best to show the actual output and why it is wrong. Saying "Output is different comparing to first query - it should be the same" is not enough, in most cases.
edit:
If you don't want to use dynamic sql (which makes sense), you could do something like this (which is actually much more readable than dynamic):
SELECT Number
, ISNULL(SUM(data),0) as other
FROM (
SELECT Number
, CASE WHEN [Group] IN (SELECT grupa FROM [dbo].[GT] WHERE typ = #typ)
THEN ISNULL(CAST([Quantity] AS INT), 0)
END data
FROM [dbo].[test-table]
) TT
GROUP BY Number
I am making a lot of assumptions about your goal, your schema and your data here though. Without more info, this is probably the best I can do...(There may be some performance tweaking to be done, but this is the essence)
Try to use a function for split the string:
CREATE FUNCTION [dbo].[FN_SplitString](#String nvarchar(4000), #Delimiter char(1))
RETURNS #Results TABLE (Items nvarchar(4000))
AS
BEGIN
IF #String IS NULL RETURN
IF LTRIM(RTRIM(#String)) = '' RETURN
DECLARE #INDEX INT
DECLARE #SLICE nvarchar(4000)
SELECT #INDEX = 1
WHILE #INDEX !=0
BEGIN
SELECT #INDEX = CHARINDEX(#Delimiter,#STRING)
IF #INDEX !=0
SELECT #SLICE = LEFT(#STRING,#INDEX - 1)
ELSE
SELECT #SLICE = #STRING
INSERT INTO #Results(Items) VALUES(#SLICE)
SELECT #STRING = RIGHT(#STRING,LEN(#STRING) - #INDEX)
IF LEN(#STRING) = 0 BREAK
END
RETURN
Then use it in this way:
SELECT
Number
,isnull(sum((case when [Group] in (SELECT Items, FROM FN_SplitString(#material,','))
then isnull(cast([Quantity] as int),0) end)),0) as other
from [dbo].[test-table]
In SQL Server, if you pass a comma separated list as a variable in IN Clause in T-SQL, it would not give error but you will not even get expected result either.
There are two solutions to handle this:
Using Dynamic query
Using Split function
I've been searching high and low for an answer and I can't seem to find anything that points me in the right direction.
I need to create a UDF that will extract each word of a text string and return a table with each word of the string in a separate row.
The UDF is only able to take in one variable '#mytext'.
We can assume that the text string is separated by a single space and may contain commas or periods.
For example, "don’t worry about failures, worry about the chances you miss when you don’t even try." would need to return a table with each word on a separate row of a column without the commas or periods present.
I'm figuring that the text string would need to be separated with a common value that could be used to separate each word for insert, but I could totally be wrong.
Any help with this would be really appreciated!
Based on what I've said so far, here is my far from complete code that I'm not too sure how to proceed with
create function [dbo].[textConverter]
(
#mytext nvarchar(max)
)
returns #text_string table
(
word nvarchar
)
as
begin
set #mytext = replace(#mytext, 'what needs to be changed', 'what it needs to be changed too')
--insert string to table
end
EDIT
I've checked out a couple of links and uncovered a little more information on this, I've now got this code. However it exits with an error. The example that was used in the article I found the code on used numbers in the insert so maybe this is the issue??
create function [dbo].[textConverter]
(
#mytext varchar(max)
)
returns #text_string table
(
word nvarchar
)
as
begin
--Change string to be seperated by commas
set #mytext = replace(#mytext, ' ', ',')
set #mytext = replace(#mytext, '.',',')
--Eliminate double commas
set #mytext = replace(#mytext, ',,', ',')
declare #name nvarchar(255)
declare #pos int
while CHARINDEX(',', #mytext) > 0
begin
select #pos = CHARINDEX(',', #mytext)
select #name = SUBSTRING(#mytext, 1, #pos-1)
insert into #text_string
select #name
select #mytext = SUBSTRING(#mytext, #pos+1, LEN(#mytext)-#pos)
end
insert into #text_string
select #mytext
return
end
--To use function
select * from dbo.textConverter('don’t worry about failures, worry about the chances you miss when you don’t even try.')
See the answer below, It is not in complete shape, but can be developed into a user defined function.
Declare #Sentence Varchar(max) = 'don’t worry about failures, worry about the chances you miss when you don’t even try.'
Set #Sentence = Replace(Replace(Replace(Replace(#Sentence,',',' '),'.',' '),' ',' '),' ',' ')
Declare #e int = (Select Len(#Sentence) - Len(Replace(#Sentence,' ','')))
Declare #s int = 1
Declare #Result Table(id int identity(1,1),Words varchar(max))
--Select #s,#e
While #s <= #e
begin
Insert into #Result
Select Left(#Sentence,Charindex(' ',#Sentence,1)-1)
Set #Sentence = Substring(#Sentence,Charindex(' ',#Sentence,1) + 1,Len(#Sentence) )
Set #s = #s + 1
End
Insert into #Result
Select #Sentence
Select * from #Result
Result
----+-----------
id |Words
----+-----------
1 |don’t
2 |worry
3 |about
4 |failures
5 |worry
6 |about
7 |the
8 |chances
9 |you
10 |miss
11 |when
12 |you
13 |don’t
14 |even
15 |try
----+-----------
I adapted some of the code from http://sqlperformance.com/2012/07/t-sql-queries/split-strings as my situation meant that the delimiter couldn't specified as input. I could only use on input and that was the text string. As such, the following worked for me:
create function [dbo].[textConverter]
(
#string nvarchar(max)
)
returns #output table(splitdata nvarchar(max)
)
begin
--Change string to be seperated by commas
set #string = replace(#string, ' ', ',')
set #string = replace(#string, '.',',')
--Eliminate double commas
set #string = replace(#string, ',,', ',')
declare #start int, #end int
select #start = 1, #end = charindex(',',#string)
while #start < len(#string) + 1
begin
if #end = 0
set #end = len(#string) + 1
insert into #output (splitdata)
values(substring(#string, #start, #end - #start))
set #start = #end + 1
set #end = charindex(',', #string, #start)
end
return
end
Basically I just need to get a 5 digits number that is separated by a space.
The 5 digits number can be anywhere in the varchar.
Example: I have a varchar column with this various data in SQL 2008 table
travel visa 34322 LLL001
Coffee 34332 Jakarta
FDR001 34312 Taxi cost cash
taxi cash 34321
34556 eating dinner with customer
eating dinner 34256 with customer
visa cost 34221 REF773716637366
the 5 digits number can be anywhere separated by a space
what is best to extract this?
34322
34332
34312
34556
34256
34221
Thanks
Row like this should return blank
Visa refNbr 778738878
Tried the following with no luck yet
SELECT pjtran.tr_comment
,substring(pjtran.tr_comment,PATINDEX('%[0-9]%',pjtran.tr_comment),5)
,Left(SubString(pjtran.tr_comment, PatIndex('%[0-9.-]%', pjtran.tr_comment), 50),PatIndex('%[^0-9.-]%', SubString(pjtran.tr_comment, PatIndex('%[0-9.-]%', pjtran.tr_comment), 50) + 'X')-1)
,len(pjtran.tr_comment)-len(replace(pjtran.tr_comment,' ',''))
I think I need to use a combination of counting the number of space in the varchar. and the above. but I am not sure how to do it
How about something like this?
select substring(tr_comment, patindex('%[0-9][0-9][0-9][0-9][0-9] %', tr_comment), 5) as zip5
If you want to consider that it might be at the end of the string:
select substring(tr_comment, patindex('%[0-9][0-9][0-9][0-9][0-9] %', tr_comment + ' '), 5
) as zip5
use this, this may help you
select SUBSTRING(tr_comment, PATINDEX('%[0-9]%', tr_comment), 5) as zip
Please Try It
CREATE FUNCTION [dbo].[udf_ExtractNumberFromString]
(
#pInputString VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #OutputString varchar(MAX)=''
DECLARE #string varchar(MAX)
DECLARE #start INT
DECLARE #end INT
DECLARE #len INT
SET #string=#pInputString
--SET #string = 'the22478hollies12345TestAddressDr.789324-#345'
SET #string = replace(#string, ' ' , '')
WHILE PATINDEX('%[0-9]%',#string) <> 0
BEGIN
SET #len = len(#string)
-- PRINT #len
set #start = PATINDEX('%[0-9]%',#string)
-- PRINT #start
SET #end= PATINDEX('%[^0-9]%',SUBSTRING(#string,#start,#len-#start))
-- PRINT #end
IF #end=0
BEGIN
SET #end=#len-#start
SET #OutputString=SUBSTRING(#string,#start,#end+1)+'-'+#OutputString
BREAK
END
ELSE
BEGIN
SET #OutputString=SUBSTRING(#string,#start,#end-1)+'-'+#OutputString
SET #string=SUBSTRING(#string,#end+#start-1,#len-#end)
END
--PRINT #string
--PRINT #Output
--PRINT '---------------------'
END
IF LEN(#OutputString)>0
SET #OutputString=LEFT(#OutputString,LEN(#OutputString)-1)
--PRINT #OutputString
RETURN #OutputString
END
My table has one column that contain strings like: ” HRM_APPLICATION_DELAY_IN”
I want to perform bellow operations on each row on this column
convert to lower case
remove underscore “_”
change case (convert to upper case) of the character after the underscore like: ” hrm_Application_Delay_In”
Need help for conversion. Thanks for advance
Here is a function to achieve it:
create function f_test
(
#a varchar(max)
)
returns varchar(max)
as
begin
set #a = lower(#a)
while #a LIKE '%\_%' ESCAPE '\'
begin
select #a = stuff(#a, v, 2, upper(substring(#a, v+1,1)))
from (select charindex('_', #a) v) a
end
return #a
end
Example:
select dbo.f_test( HRM_APPLICATION_DELAY_IN')
Result:
hrmApplicationDelayIn
To update your table here is an example how to write the syntax with the function:
UPDATE <yourtable>
SET <yourcolumn> = dbo.f_test(col)
WHERE <yourcolumn> LIKE '%\_%' ESCAPE '\'
For a variable this is overkill, but I'm using this to demonstrate a pattern
declare #str varchar(100) = 'HRM_APPLICATION_DELAY_IN';
;with c(one,last,rest) as (
select cast(lower(left(#str,1)) as varchar(max)),
left(#str,1), stuff(lower(#str),1,1,'')
union all
select one+case when last='_'
then upper(left(rest,1))
else left(rest,1) end,
left(rest,1), stuff(rest,1,1,'')
from c
where rest > ''
)
select max(one)
from c;
That can be extended to a column in a table
-- Sample table
declare #tbl table (
id int identity not null primary key clustered,
str varchar(100)
);
insert #tbl values
('HRM_APPLICATION_DELAY_IN'),
('HRM_APPLICATION_DELAY_OUT'),
('_HRM_APPLICATION_DELAY_OUT'),
(''),
(null),
('abc<de_fg>hi');
-- the query
;with c(id,one,last,rest) as (
select id,cast(lower(left(str,1)) as varchar(max)),
left(str,1), stuff(lower(str),1,1,'')
from #tbl
union all
select id,one+case when last='_'
then upper(left(rest,1))
else left(rest,1) end,
left(rest,1), stuff(rest,1,1,'')
from c
where rest > ''
)
select id,max(one)
from c
group by id
option (maxrecursion 0);
-- result
ID COLUMN_1
1 hrm_Application_Delay_In
2 hrm_Application_Delay_Out
3 _Hrm_Application_Delay_Out
4
5 (null)
6 abc<de_Fg>hi
select
replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(replace(lower('HRM_APPLICATION_DELAY_IN'),'_a','A'),'_b','B'),'_c','C'),'_d','D'),'_e','E'),'_f','F'),
'_g','G'),'_h','H'),'_i','I'),'_j','J'),'_k','K'),'_l','L'),
'_m','M'),'_n','N'),'_o','O'),'_p','P'),'_q','Q'),'_r','R'),
'_s','S'),'_t','T'),'_u','U'),'_v','V'),'_w','W'),'_x','X'),
'_y','Y'),'_z','Z'),'_','')
Bellow two steps can solve problem,as example i use sys.table.user can use any one
declare #Ret varchar(8000), #RetVal varchar(8000), #i int, #count int = 1;
declare #c varchar(10), #Text varchar(8000), #PrevCase varchar, #ModPrefix varchar(10);
DECLARE #FileDataTable TABLE(TableName varchar(200))
INSERT INTO #FileDataTable
select name FROM sys.tables where object_name(object_id) not like 'sys%' order by name
SET #ModPrefix = 'Pur'
DECLARE crsTablesTruncIns CURSOR
FOR select TableName FROM #FileDataTable
OPEN crsTablesTruncIns
FETCH NEXT FROM crsTablesTruncIns INTO #Text
WHILE ##FETCH_STATUS = 0
BEGIN
SET #RetVal = '';
select #i=1, #Ret = '';
while (#i <= len(#Text))
begin
SET #c = substring(#Text,#i,1)
--SET #Ret = #Ret + case when #Reset=1 then UPPER(#c) else LOWER(#c)
IF(#PrevCase = '_' OR #i = 1)
SET #Ret = UPPER(#c)
ELSE
SET #Ret = LOWER(#c)
--#Reset = case when #c like '[a-zA-Z]' then 0 else 1 end,
if(#c like '[a-zA-Z]')
SET #RetVal = #RetVal + #Ret
if(#c = '_')
SET #PrevCase = '_'
else
SET #PrevCase = ''
SET #i = #i +1
end
SET #RetVal = #ModPrefix + #RetVal
print cast(#count as varchar) + ' ' + #RetVal
SET #count = #count + 1
EXEC sp_RENAME #Text , #RetVal
SET #RetVal = ''
FETCH NEXT FROM crsTablesTruncIns INTO #Text
END
CLOSE crsTablesTruncIns
DEALLOCATE crsTablesTruncIns
I'd like to show you my nice and simple solution. It uses Tally function to split the string by pattern, in our case by underscope. For understanding Tally functions, read this article.
So, this is how my tally function looks like:
CREATE FUNCTION [dbo].[tvf_xt_tally_split](
#String NVARCHAR(max)
,#Delim CHAR(1))
RETURNS TABLE
as
return
(
WITH Tally AS (SELECT top (select isnull(LEN(#String),100)) n = ROW_NUMBER() OVER(ORDER BY [name]) from master.dbo.syscolumns)
(
SELECT LTRIM(RTRIM(SUBSTRING(#Delim + #String + #Delim,N+1,CHARINDEX(#Delim,#Delim + #String + #Delim,N+1)-N-1))) Value, N as Ix
FROM Tally
WHERE N < LEN(#Delim + #String + #Delim)
AND SUBSTRING(#Delim + #String + #Delim,N,1) = #Delim
)
)
This function returns a table, where each row represents part of string between #Delim (in our case between underscopes). Rest of the work is simple, just cobination of LEFT, RIGHT, LEN, UPPER and LOWER functions.
declare #string varchar(max)
set #string = ' HRM_APPLICATION_DELAY_IN'
-- convert to lower case
set #string = LOWER(#string)
declare #output varchar(max)
-- build string
select #output = coalesce(#output + '_','') +
UPPER(left(Value,1)) + RIGHT(Value, LEN(Value) - 1)
from dbo.tvf_xt_tally_split(#string, '_')
-- lower first char
select left(lower(#output),1) + RIGHT(#output, LEN(#output) - 1)