String parsing | SQL Server Performance Forums

SQL Server Performance Forum – Threads Archive

String parsing

Hi,<br />Can someone plz tell me what is a fast way to do string parsing in T-SQL? Heres my problem:<br /><br />One of the tables I have contains a rule_id column which looks like:<br />attribute1_val:attribute2_val:attribute3_val:…<br /><br />The total number of inter attributes is always the same.<br /><br />There can also be intra values like:<br /><br />attribute1_val,attribute1_val11,attribute1_val12:attribute2_val:attribute3_val:…<br /><br />These attributes are essentially user attributes and another table has the attributes for a user.<br /><br />So, in the rules table, I can have a column value like:<br />2<img src=’/community/emoticons/emotion-2.gif’ alt=’:D‘ />A:1,2:#: .. etc<br />which means that show this content to user whose <br />attribute_1 is 2<br />attribute_2 is DA<br />attribute_3 is in (1,2,3) and so on..<br />attribute_4 is # (which essentially menas ignore this attribute since this)<br /><br />Can someone help me with a parsing query to match this up?<br /><br />I was also thinking of splitting the rules table into another table which has all these attributes in column (and which can be indexed). Is that a better option than running the parsing script?<br /><br />Pls let me know if I have not been able to explain this fully since this is a little bit tricky.<br /><br />Thanx<br />Nitin <br /><br /><br />
You would definitely need to explain a bit more, but from what I understand now, your table violates First Normal Form. A column should only contain just one scalar value, not an array for values. T-SQL isn’t the language of choice for string parsing. You should really consider having another table for that data. —
Frank Kalis
Microsoft SQL Server MVP
http://www.insidesql.de
Ich unterstütze PASS Deutschland e.V. http://www.sqlpass.de)

Hi Frank, Thanks for the response. Heres the thing. The base structure of the table I cannot change because of a third party’s limitations which is holding these values in a colon separated manner. Currently we are going through a lot of looping etc so that at run time, the user’s sttributes are matched against these parsed values for different attributes. I was thinking of not doing this at run time and creating a master table where I have the rule_id and user_id in one table and then at run time I join the rule_id in master table with the rule_id in original table and get the results faster. Your thoughts, Nitin

I once built a system that had a fixed-length alphanumeric code with some business rules built-in – don’t ask why, that’s what the client wanted. For basic ‘data integrity checking’ I had an auxiliary table that listed each valid entry for each position. The checking was done by reconstructing the original code like this: SET @Code = ‘ABC123’
SET @Check = ” SELECT @Check = @Check + aux.Entry
FROM AuxiliaryTable AS aux
WHERE SUBSTRING(@Code, aux.Position, 1) = aux.Entry
ORDER BY aux.Position IF @Code <> @Check
BEGIN
— check failed
END Another table listed valid combinations of characters, in strings that had the wildcard format exactly like you would use in a LIKE clause (use ! for solving the NOT issue – and lots of square brackets) – plus the starting point and number of positions of the matching string in the code. We verified that the code entered was matching all the wildcard strings – I seem to remember lots of double-negative syntax in EXISTS clauses because there would be a wildcard string for a set of possible values at position X and then one for another set of possible values at position X, and so on and so on. It took a lot of to-and-fro with the client to get the business rules right – and it turned out their system needed a bit of cleaning-up, but we got there in the end.
nattynatty, your vendor has a very unfortunate design there. The data from that column should be split across one column per value. If it were me, I guess I would look at using the CHARINDEX() function to locate the delimiters, then SUBSTRING() to pull the value from between them.
I have created a split function:<br />CREATE FUNCTION pfn_split<br />(<br /> @list nvarchar(2000),<br /> @spliton nvarchar(5),<br /> @whichsplit int<br />) <br />returns nvarchar(1000)<br />as <br /><br />begin<br />declare @mylist nvarchar(1000)<br />declare @value nvarchar(1000)<br />declare @num int<br />declare @intra_spliton nvarchar(5)<br />set @mylist = ”<br />set @num=0<br />set @intra_spliton=’:'<br /><br />set @mylist = @list<br /><br />while (charindex(@spliton, @list)&gt;0)<br />begin <br /> <br />set @value = ltrim(rtrim(substring(@list,1,charindex(@spliton,@list)-1))) <br />set @mylist = @value<br />set @num = @num + 1<br /><br />if @num = @whichsplit<br />break<br /> set @list = substring(@list,charindex(@spliton,@list)+len(@spliton),len(@list))<br /><br />end<br /><br /><br />if @num = @whichsplit<br />begin<br /> set @mylist = @mylist<br />end<br />else<br />begin<br /> set @mylist = substring(@list,charindex(@spliton, @list)+len(@spliton),len(@list)) <br />end<br /><br /><br />return @mylist<br />end<br /><br /><br />The return string is like this:<br /><br />select dbo.pfn_split(‘100;4;SE;#;#;#;#;#;#;#’,’;’,2) =&gt; 4<br />select dbo.pfn_split(‘100;4:5;SE;#;#;#;#;#;#;#’,’;’,2) =&gt; 4:5<br /><br />Now the way I am calling this in my T-sql code:<br /><br />————————————————<br /><br />declare<br />@rules_string varchar(1000),<br />@rules_ex_string varchar(1000),<br />@role_string varchar(1000),<br />@cluster_string varchar(1000),<br />@division_string varchar(1000),<br />@region_string varchar(1000),<br />@district_string varchar(1000),<br />@dce_string varchar(1000),<br />@masters_string varchar(1000),<br />@state_string varchar(1000)<br /><br />begin<br /><br />set @rules_string=’100;3;SE;#;#;#;#;#;#;#'<br /><br />set @role_string=dbo.pfn_split(@rules_string,’;’,1)<br />set @cluster_string=dbo.pfn_split(@rules_string,’;’,2)<br />set @division_string=dbo.pfn_split(@rules_string,’;’,3)<br />set @region_string=dbo.pfn_split(@rules_string,’;’,4)<br />set @district_string=dbo.pfn_split(@rules_string,’;’,5)<br />set @dce_string=dbo.pfn_split(@rules_string,’;’,7)<br />set @masters_string=dbo.pfn_split(@rules_string,’;’,<img src=’/community/emoticons/emotion-11.gif’ alt=’8)’ /><br />set @state_string=dbo.pfn_split(@rules_string,’;’,9)<br /><br /><br />select * from pfn_app_prd.VW_PFN_FIELD_REP a<br />where a.role_cd in (replace(@role_string,’#’,a.role_cd))<br />and a.cluster_cd in (replace(@cluster_string,’#’,a.cluster_cd))<br />and a.division_cd in (replace(@division_string,’#’,a.division_cd))<br />and a.region_cd in (replace(@region_string,’#’,a.region_cd))<br />and a.district_cd in (replace(@district_string,’#’,a.district_cd))<br />and a.is_dce in (replace(@dce_string,’#’,a.is_dce))<br />and a.masters_fg in (replace(@masters_string,’#’,a.masters_fg))<br />and a.mailing_state in (replace(@state_string,’#’,a.mailing_state))<br /><br />This works fine when there are no intra value separators but when there are intra value separators then matching against any of the user attributes does not return proper results since the IN condition is looking for single quote separated values for varchar.<br /><br />In other words if the rule_id is: ‘100;4:5;SE;#;#;#;#;#;#;#'<br />then the cluster string will be : 4:5 which will always fail since the equate condition ina.cluster_cd should be something like (‘4′,’5’). Now I can try using the pfn_split function again but for that I need to loop and I dont really like that idea.<br /><br />Suggestions/Inputs..<br /><br />Thanx<br />Nitin <br />
Hey, you didn’t tell in your first post that you are stuck with the design. [<img src=’/community/emoticons/emotion-5.gif’ alt=’;)‘ />]<br />Probably this one:<a target="_blank" href=http://www.sommarskog.se/arrays-in-sql.html>http://www.sommarskog.se/arrays-in-sql.html</a> will help you determining the "best" approach to split a string.<br /><br />–<br />Frank Kalis<br />Microsoft SQL Server MVP<br /<a target="_blank" href=http://www.insidesql.de>http://www.insidesql.de</a><br />Ich unterstütze PASS Deutschland e.V. <a target="_blank" href=http://www.sqlpass.de>http://www.sqlpass.de</a>) <br />
]]>