Log in  ::  Register  ::  Not logged in

## Find integer at end of a string. There *must* be a better solution ?

 Author Message Tom Thomson SSC Guru Group: General Forum Members Points: 50305 Visits: 13146 Thorkil Johansen (7/25/2013)Someone said: Looks like 123.4 is not an integer, so -1 should be returned.Yes, that's rightSo if the string were xyz-1-2.3-2 what's the required answer? 2, because -2 are the last two characters? or -1 because -1-2.3-2 isn't an integer? Tom Thorkil Johansen Valued Member Group: General Forum Members Points: 53 Visits: 64 Hi TomI have the assumption that only one hyphen will be in the string, but if the case was xyz-1-2.3-2 then 2 would be the right answer...To sharpen a little: "The integer to the right of the last hyphen in the string"/T dwain.c SSC-Forever Group: General Forum Members Points: 43693 Visits: 6431 My contribution ain't exactly pretty but might work for you:`with tmp as ( select x = 'some text-1'union select x = 'some text-123'union select x = 'some text-123.4'union select x = 'some text 123'union select x = 'some text-'union select x = 'some text'union select x = 'some text-xyz')SELECT x, CASE WHEN EndStr IN (0,1) THEN -1 WHEN 0=ISNUMERIC(RIGHT(x, EndStr-1)) THEN -1 WHEN FLOOR(RIGHT(x, EndStr-1)) = RIGHT(x, EndStr-1) THEN CAST(RIGHT(x, EndStr-1) AS INT) ELSE -1 ENDFROM tmp aCROSS APPLY (SELECT CHARINDEX('-', REVERSE(x))) b(EndStr);` My mantra: No loops! No CURSORs! No RBAR! Hoo-uh!My thought question: Have you ever been told that your query runs too fast?My advice:INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.Need to UNPIVOT? Why not CROSS APPLY VALUES instead?Since random numbers are too important to be left to chance, let's generate some!Learn to understand recursive CTEs by example.Splitting strings based on patterns can be fast!My temporal SQL musings: Calendar Tables, an Easter SQL, Time Slots and Self-maintaining, Contiguous Effective Dates in Temporal Tables Cadavre SSC-Insane Group: General Forum Members Points: 21770 Visits: 8519 Forgive me for my simplistic approach :-D`WITH tmpAS (SELECT x = 'some text-1' UNION ALL SELECT x = 'some text-123' UNION ALL SELECT x = 'some text-123.4' UNION ALL SELECT x = 'some text 123' UNION ALL SELECT x = 'some text-' UNION ALL SELECT x = 'some text' UNION ALL SELECT x = 'some text-xyz' UNION ALL SELECT x = 'xyz-1-2.3-2' )SELECT *, CASE WHEN x LIKE '%-[0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN REVERSE(SUBSTRING(REVERSE(x),0,CHARINDEX('-',REVERSE(x)))) ELSE - 1 ENDFROM tmp;`--EDIT--Quick performance check. Note, I borrowed Dwain's method for grabbing the last digits as I figured it'd be quicker than the reverse reverse as SQL is not good at string manipulation.`SET NOCOUNT ON;IF object_id('tempdb..#testEnvironment') IS NOT NULLBEGIN DROP TABLE #testEnvironment;END; --1,000,000 Random rows of dataSELECT TOP 1000000 IDENTITY(INT,1,1) AS ID, 'some text'+ CASE WHEN (ABS(CHECKSUM(NEWID())) % 100) + 1 >= 50 THEN '-' ELSE '' END +CASE WHEN (ABS(CHECKSUM(NEWID())) % 100) + 1 >= 50 AND CHARINDEX('.',CAST(RAND(CHECKSUM(NEWID())) * 100 AS VARCHAR(MAX))) > 3 THEN SUBSTRING(CAST(RAND(CHECKSUM(NEWID())) * 100 AS VARCHAR(MAX)),0,CHARINDEX('.',CAST(RAND(CHECKSUM(NEWID())) * 100 AS VARCHAR(MAX)))-1) ELSE CAST(RAND(CHECKSUM(NEWID())) * 100 AS VARCHAR(MAX)) END AS xINTO #testEnvironmentFROM master.dbo.syscolumns sc1, master.dbo.syscolumns sc2, master.dbo.syscolumns sc3;DECLARE @Loop CHAR(1) = '0', @HOLDER INT, @Duration CHAR(12), @StartTime DATETIME;WHILE @Loop <= 5BEGIN RAISERROR('Loop: %s',0,1,@Loop) WITH NOWAIT; RAISERROR('============',0,1) WITH NOWAIT; DBCC FREEPROCCACHE WITH NO_INFOMSGS; DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS; SELECT @StartTime = GETDATE();SELECT @HOLDER = CASE WHEN x LIKE '%-[0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) WHEN x LIKE '%-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN RIGHT(x,CHARINDEX('-',REVERSE(x))-1) ELSE - 1 ENDFROM #testEnvironment; SELECT @Duration = CONVERT(CHAR(12),GETDATE()-@StartTime,114); RAISERROR('BIG CASE Duration: %s',0,1,@Duration) WITH NOWAIT; DBCC FREEPROCCACHE WITH NO_INFOMSGS; DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS; SELECT @StartTime = GETDATE(); SELECT @HOLDER = CASE WHEN EndStr IN (0,1) THEN -1 WHEN 0=ISNUMERIC(RIGHT(x, EndStr-1)) THEN -1 WHEN FLOOR(RIGHT(x, EndStr-1)) = RIGHT(x, EndStr-1) THEN CAST(RIGHT(x, EndStr-1) AS INT) ELSE -1 ENDFROM #testEnvironment aCROSS APPLY (SELECT CHARINDEX('-', REVERSE(x))) b(EndStr); SELECT @Duration = CONVERT(CHAR(12),GETDATE()-@StartTime,114); RAISERROR('COOL MATHS Duration: %s',0,1,@Duration) WITH NOWAIT; SET @Loop = @Loop + 1;END``Loop: 0============BIG CASE Duration: 00:00:51:533COOL MATHS Duration: 00:00:12:057Loop: 1============BIG CASE Duration: 00:00:52:107COOL MATHS Duration: 00:00:11:967Loop: 2============BIG CASE Duration: 00:00:53:087COOL MATHS Duration: 00:00:12:240Loop: 3============BIG CASE Duration: 00:00:52:077COOL MATHS Duration: 00:00:12:100Loop: 4============BIG CASE Duration: 00:00:51:477COOL MATHS Duration: 00:00:11:970Loop: 5============BIG CASE Duration: 00:00:52:023COOL MATHS Duration: 00:00:12:010`Pretty consistent win for Dwain's solution over mine. The results show a similar pattern with an index (big case took around 50 seconds but the maths reduced down to around 7 seconds). Forever trying to learnFor better, quicker answers on T-SQL questions, click on the following...http://www.sqlservercentral.com/articles/Best+Practices/61537/For better, quicker answers on SQL Server performance related questions, click on the following...http://www.sqlservercentral.com/articles/SQLServerCentral/66909/If you litter your database queries with nolock query hints, are you aware of the side effects?Try reading a few of these links...(*) Missing rows with nolock(*) Allocation order scans with nolock(*) Consistency issues with nolock(*) Transient Corruption Errors in SQL Server error log caused by nolock(*) Dirty reads, read errors, reading rows twice and missing rows with nolockCraig Wilkinson - Software EngineerLinkedIn Lowell SSC Guru Group: General Forum Members Points: 181609 Visits: 41569 Cadavre I love your new signature with the nolock pointers! just noticed it today. Lowell--help us help you! If you post a question, make sure you include a CREATE TABLE... statement and INSERT INTO... statement into that table to give the volunteers here representative data. with your description of the problem, we can provide a tested, verifiable solution to your question! asking the question the right way gets you a tested answer the fastest way possible! Cadavre SSC-Insane Group: General Forum Members Points: 21770 Visits: 8519 Lowell (7/26/2013)Cadavre I love your new signature with the nolock pointers! just noticed it today.I added it after the last time I had to dig them all out :-) Forever trying to learnFor better, quicker answers on T-SQL questions, click on the following...http://www.sqlservercentral.com/articles/Best+Practices/61537/For better, quicker answers on SQL Server performance related questions, click on the following...http://www.sqlservercentral.com/articles/SQLServerCentral/66909/If you litter your database queries with nolock query hints, are you aware of the side effects?Try reading a few of these links...(*) Missing rows with nolock(*) Allocation order scans with nolock(*) Consistency issues with nolock(*) Transient Corruption Errors in SQL Server error log caused by nolock(*) Dirty reads, read errors, reading rows twice and missing rows with nolockCraig Wilkinson - Software EngineerLinkedIn Erland Sommarskog SSChampion Group: General Forum Members Points: 13148 Visits: 879 `with tmp as ( select x = 'some text-1'union select x = 'some text-123'union select x = 'some text-123.4'union select x = 'some text 123'union select x = 'some text-'union select x = 'some text'union select x = 'some text-xyz')select * , coalesce( TRY_CONVERT(int, nullif(SUBSTRING(x, 1 + nullif(CHARINDEX('-',x), 0), LEN(x)), '')), -1) from tmp`LEN(x) - charindex can be reduced to LEN(x), since you want the rest of the string. I removed the outer CASE and replaced it with two NULLIF. One to capture that therte are no hyphen at all, and one to capture that there are only spaces after the hyphen.There was a question about multiple hyphens. My assumptions from the original post is that the integer should be from the hyphen to the end of the string, so Abc-2-3 should return -1. Erland Sommarskog, SQL Server MVP, www.sommarskog.se dwain.c SSC-Forever Group: General Forum Members Points: 43693 Visits: 6431 Cadavre - I love the name you gave mine: "COOL MATHS/"I suspect it might have a bug though where the string ends like -\$ (might show 0). My mantra: No loops! No CURSORs! No RBAR! Hoo-uh!My thought question: Have you ever been told that your query runs too fast?My advice:INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.Need to UNPIVOT? Why not CROSS APPLY VALUES instead?Since random numbers are too important to be left to chance, let's generate some!Learn to understand recursive CTEs by example.Splitting strings based on patterns can be fast!My temporal SQL musings: Calendar Tables, an Easter SQL, Time Slots and Self-maintaining, Contiguous Effective Dates in Temporal Tables Thorkil Johansen Valued Member Group: General Forum Members Points: 53 Visits: 64 Nice one... :-)I have the assumption that only one hyphen will be in the string, but if the case was xyz-1-2.3-2 then 2 would be the right answer...To sharpen a little: "The integer to the right of the last hyphen in the string" Jeff Moden SSC Guru Group: General Forum Members Points: 503894 Visits: 44223 The use of IsNumeric in onne of the solutions serves as a reminder that there are a whole lot of things that look like numbers to SQLServer. Here's some updated test data to see what I mean...`with tmp as ( SELECT x = 'some text-1' UNION ALL SELECT x = 'some text-123' UNION ALL SELECT x = 'some text-123.4' UNION ALL SELECT x = 'some text 123' UNION ALL SELECT x = 'some text-' UNION ALL SELECT x = 'some text- ' --Added this for possible CHAR values UNION ALL SELECT x = 'some text- 123 ' --Added "stray bullet" UNION ALL SELECT x = 'some text-123 456' --Added "stray bullet" UNION ALL SELECT x = 'some text-123d4' --Added "stray bullet" UNION ALL SELECT x = 'some text-123e4' --Added "stray bullet" UNION ALL SELECT x = 'some text-\$1,234' --Added "stray bullet" UNION ALL SELECT x = 'some text-1,234' --Added "stray bullet" UNION ALL SELECT x = 'some text-0x1234,' --Added "stray bullet" UNION ALL SELECT x = 'some text' UNION ALL SELECT x = 'some text-xyz')... put your code here ...` --Jeff ModenRBAR is pronounced ree-bar and is a Modenism for Row-By-Agonizing-Row.First step towards the paradigm shift of writing Set Based code: Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column.If you think its expensive to hire a professional to do the job, wait until you hire an amateur. -- Red Adair Helpful Links:How to post code problemsHow to post performance problemsForum FAQs