Extract String Between Two Different Characters - sql

I am having some trouble trying to figure out a way to extract a string between two different characters. My issue here is that the column (CONFIG_ID) contains more that 75,000 rows and the format is not consistent, so I cannot figure out a way to get the numbers between E and B.
*CONFIG_ID*
6E15B1P
999E999B1P
1E3B1P
1E30B1P
5E24B1P
23E6B1P

Another option is to use a CROSS APPLY to calculate the values only once. Another nice thing about CROSS APPLY is that you can stack calculations and use them in the top SELECT
Notice the nullif() rather than throwing an error if the character is not found, it will return a NULL
THIS ALSO ASSUMES there are no LEADING B's
Example
Declare #YourTable Table ([CONFIG_ID] varchar(50)) Insert Into #YourTable Values
('6E15B1P')
,('999E999B1P')
,('1E3B1P')
,('1E30B1P')
,('5E24B1P')
,('23E6B1P')
,('23E6ZZZ') -- Notice No B
Select [CONFIG_ID]
,NewValue = substring([CONFIG_ID],P1,P2-P1)
From #YourTable
Cross Apply ( values (nullif(charindex('E',[CONFIG_ID]),0)+1
,nullif(charindex('B',[CONFIG_ID]),0)
) )B(P1,P2)
Results
CONFIG_ID NewValue
6E15B1P 15
999E999B1P 999
1E3B1P 3
1E30B1P 30
5E24B1P 24
23E6B1P 6
23E6ZZZ NULL -- Notice No B

SUBSTRING(config_id,PATINDEX('%E%',config_id)+1,PATINDEX('%B%',config_id)-PATINDEX('%E%',config_id)-1)
As in:
WITH dat
AS
(
SELECT config_id
FROM (VALUES ('1E30B1P')) t(config_id)
)
SELECT SUBSTRING(config_id,PATINDEX('%E%',config_id)+1,PATINDEX('%B%',config_id)-PATINDEX('%E%',config_id)-1)
FROM dat

A cased substring of a left could be enough.
select *
, CASE
WHEN [CONFIG_ID] LIKE '%E%B%'
THEN SUBSTRING(LEFT([CONFIG_ID], CHARINDEX('B',[CONFIG_ID],CHARINDEX('E',[CONFIG_ID]))),
CHARINDEX('E',[CONFIG_ID]), LEN([CONFIG_ID]))
END AS [CONFIG_EB]
from Your_Table
CONFIG_ID
CONFIG_EB
6E15B1P
E15B
999E999B1P
E999B
1E3B1P
E3B
1E30B1P
E30B
5E24B1P
E24B
23E6B1P
E6B
23E678
null
236789
null
23B456
null
Test on db<>fiddle here

Related

How to get string from value

I have an Customer_value column.
The column contains values like:
DAL123245,HC.533675,ABC.01232423
HC.3425364,ABC.045367544,DAL4346456
HC.35344,ABC.03543645754,ABC.023534454,DAL.4356433
ABC.043534553,HC.3453643,ABC.05746343
What I am trying to do is get the number after the first "ABC.0" string.
For example, this is what I would like to get:
1232423
5367544
3543645754
43534553
this is what I tried:
Substring(customer_value,charindex('ABC.', customer_value) + 5, len(customer_value)) as dataneeded
The issue that I got is for 1 and 2 I got that right data as needed, but for 3 and 4, because there are multiple ABC so it gave me everything after the first ABC.
How can I get the number after the first ABC. only?
Thank you so much
Just another option is to use a bit of JSON to parse and preserve the sequence in concert with a CROSS APPLY
Note: Use OUTER APPLY to see NULL values
Example
Select NewVal = replace(Value,'ABC.0','')
From YourTable A
Cross Apply (
Select Top 1 *
From OpenJSON( '["'+replace(string_escape(customer_value,'json'),',','","')+'"]' )
Where Value like 'ABC.0%'
Order by [key]
) B
Results
NewVal
1232423
45367544
3543645754
43534553
On the assumption you are using SQL Server (given your use of charindex()/substring()/len()) you can use apply to calculate the starting position and then find the next occurence utilising the start position optional parameter of charindex, then get the substring between the values.
select Substring(customer_value, p1.v, Abs(p2.v-p1.v)) as dataneeded
from t
cross apply(values(charindex('ABC.', customer_value)+5))p1(v)
cross apply(values(charindex(',', customer_value,p1.v)))p2(v)

concat two strings and put smaller string at first in sql server

for concating two varchars from columns A and B ,like "1923X" and "11459" with the hashtag, while I always want the smallest string become at first, what should I do in SQL server query?
inputs:
Two Columns
A="1923X"
B="11459"
procedure:
while we are checking two inputs from right to left, in this example the second character value in B (1) is smaller than the second character in A (9) so B is smaller.
result: new column C
"11459#1923X"
Original answer:
If you need to order the input strings, not only by second character, STRING_AGG() is also an option:
DECLARE #a varchar(5) = '1923X'
DECLARE #b varchar(5) = '11459'
SELECT STRING_AGG(v.String, '#') WITHIN GROUP (ORDER BY v.String) AS Result
FROM (VALUES (#a), (#b)) v (String)
Output:
Result
11459#1923X
Update:
You changed the requirements (now the strings are stored in two columns), so you need a different statement:
SELECT
A,
B,
C = (
SELECT STRING_AGG(v.String, '#') WITHIN GROUP (ORDER BY v.String)
FROM (VALUES (A), (B)) v (String)
)
FROM (VALUES ('1923X', '11459')) t (a, b)

Filter IDs with just numbers excluding letters

So I have results that begins with 2 letters followed by 3 numbers, for example:
ID_Sample
AB001
BC003
AB100
BC400
How can I do a query that ignores the letters and just looks up the numbers to do a filter? For example:
WHERE ID_Sample >= 100
I tried using a "Replace" to get rid of known letters, but I figured there might be a better way. For example:
Select
Replace(id_sample,'AB','')
Choosing the 3 numerals on the right would work too.
For your sample data, you can just start at the third character and convert to a number:
where try_convert(int, stuff(ID_Sample, 1, 2, '')) > 100
Or, if you know that the number is 3 characters:
where try_convert(int, right(ID_Sample, 3)) > 100
+1 for Gordon's answer. This is a fun problem that you can solve using TRANSLATE if you're using SQL 2017+.
First, in case you've never used it, Per BOL TRANSLATE:
Returns the string provided as a first argument after some characters
specified in the second argument are translated into a destination set
of characters specified in the third argument.2
This:
SELECT TRANSLATE('123AABBCC!!!','ABC','XYZ');
Returns: 123XXYYZZ!!!
Here's the solution using TRANSLATE:
-- Sample Data
DECLARE #t TABLE (ID_Sample CHAR(6))
INSERT #t (ID_Sample) VALUES ('AB001'),('BC003'),('AB100'),('BC400'),('CC555');
-- Solution
SELECT
ID_Sample = t.ID_Sample,
ID_Sample_Int = s.NewString
FROM #t AS t
CROSS JOIN (VALUES('ABCDEFGHIJKLMNOPQRSTUVWXYZ', REPLICATE(0,26))) AS f(S1,S2)
CROSS APPLY (VALUES(TRY_CAST(TRANSLATE(t.ID_Sample,f.S1,f.S2) AS INT))) AS s(NewString)
WHERE s.NewString >= 100;
Without the WHERE clause filter you get:
ID_Sample ID_Sample_Int
--------- -------------
AB001 1
BC003 3
AB100 100
BC400 400
CC555 555
... the WHERE clause filters out the first two rows.
Check these methods- Unit test also done!
Declare #Table as table(ID_Sample varchar(20))
set nocount on
Insert into #Table (ID_Sample)
Values('AB001'),('BC003'),('AB100'),('BC400')
--substring_method
select * from #Table
where try_cast(substring(ID_Sample,3,3) as int) >100
--right_method
select * from #Table
where try_cast(right(ID_Sample,3) as int) >100
--stuff_method
select * from #Table
where try_cast(stuff(ID_Sample,1,2,'') as int) >100
--replace_method
select * from #Table
where try_cast(replace(ID_Sample,left(ID_Sample,2),'') as int) >100

sql extract rightmost number in string and increment

i have transaction codes like
"A0004", "1B2005","20CCCCCCC21"
I need to extract the rightmost number and increment the transaction code by one
"AA0004"----->"AA0005"
"1B2005"------->"1B2006"
"20CCCCCCCC21"------>"20CCCCCCCC22"
in SQL Server 2012.
unknown length of string
right(n?) always number
dealing with unsignificant number of string and number length is out of my league.
some logic is always missing.
LEFT(#a,2)+RIGHT('000'+CONVERT(NVARCHAR,CONVERT(INT,SUBSTRING( SUBSTRING(#a,2,4),2,3))+1)),3
First, I want to be clear about this: I totally agree with the comments to the question from a_horse_with_no_name and Jeroen Mostert.
You should be storing one data point per column, period.
Having said that, I do realize that a lot of times the database structure can't be changed - so here's one possible way to get that calculation for you.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
col varchar(100)
);
INSERT INTO #T (col) VALUES
('A0004'),
('1B2005'),
('1B2000'),
('1B00'),
('20CCCCCCC21');
(I've added a couple of strings as edge cases you didn't mention in the question)
Then, using a couple of cross apply to minimize code repetition, I came up with that:
SELECT col,
LEFT(col, LEN(col) - LastCharIndex + 1) +
REPLICATE('0', LEN(NumberString) - LEN(CAST(NumberString as int))) +
CAST((CAST(NumberString as int) + 1) as varchar(100)) As Result
FROM #T
CROSS APPLY
(
SELECT PATINDEX('%[^0-9]%', Reverse(col)) As LastCharIndex
) As Idx
CROSS APPLY
(
SELECT RIGHT(col, LastCharIndex - 1) As NumberString
) As NS
Results:
col Result
A0004 A0005
1B2005 1B2006
1B2000 1B2001
1B00 1B01
20CCCCCCC21 20CCCCCCC22
The LastCharIndex represents the index of the last non-digit char in the string.
The NumberString represents the number to increment, as a string (to preserve the leading zeroes if they exists).
From there, it's simply taking the left part of the string (that is, up until the number), and concatenate it to a newly calculated number string, using Replicate to pad the result of addition with the exact number of leading zeroes the original number string had.
Try This
DECLARE #test nvarchar(1000) ='"A0004", "1B2005","20CCCCCCC21"'
DECLARE #Temp AS TABLE (ID INT IDENTITY,Data nvarchar(1000))
INSERT INTO #Temp
SELECT #test
;WITH CTE
AS
(
SELECT Id,LTRIM(RTRIM((REPLACE(Split.a.value('.' ,' nvarchar(max)'),'"','')))) AS Data
,RIGHT(LTRIM(RTRIM((REPLACE(Split.a.value('.' ,' nvarchar(max)'),'"','')))),1)+1 AS ReqData
FROM
(
SELECT ID,
CAST ('<S>'+REPLACE(Data,',','</S><S>')+'</S>' AS XML) AS Data
FROM #Temp
) AS A
CROSS APPLY Data.nodes ('S') AS Split(a)
)
SELECT CONCAT('"'+Data+'"','-------->','"'+CONCAT(LEFT(Data,LEN(Data)-1),CAST(ReqData AS VARCHAR))+'"') AS ExpectedResult
FROM CTE
Result
ExpectedResult
-----------------
"A0004"-------->"A0005"
"1B2005"-------->"1B2006"
"20CCCCCCC21"-------->"20CCCCCCC22"
STUFF(#X
,LEN(#X)-CASE PATINDEX('%[A-Z]%',REVERSE(#X)) WHEN 0 THEN LEN(#X) ELSE PATINDEX('%[A-Z]%',REVERSE(#X))-1 END+1
,LEN(((RIGHT(#X,CASE PATINDEX('%[A-Z]%',REVERSE(#X)) WHEN 0 THEN LEN(#X) ELSE PATINDEX('%[A-Z]%',REVERSE(#X))-1 END)/#N)+1)#N)
,((RIGHT(#X,CASE PATINDEX('%[A-Z]%',REVERSE(#X)) WHEN 0 THEN LEN(#X) ELSE PATINDEX('%[A-Z]%',REVERSE(#X))-1 END)/#N)+1)#N)
works on number only strings
99 becomes 100
mod(#N) increments

SQL How to convert number to text with minimum decimal places (dynamic number of decimal places) in SQL

In an SQL query, I want to convert numeric value to text with minimum no. of decimal places, example if the number is 2.50, then I want output as 2.5; if number is 3, then I want output as 3; if number is 18.75, I want output as 18.75, etc.
How can I achieve this?
EDIT 1:
To give more background, I am dividing 2 numeric values, and want the result in text with minimum required decimal places.
Thanks.
In SQL 2012 and above you can write
SELECT FORMAT(15.0/4.0 , '#.########' )
It uses FORMAT function which uses .NET String.Format functionality.
If want to get 2 decimals for division values, use this
Select case when right(cast ( x/y as decimal(18,2)),1) = 0
then left (cast ( x/y as decimal(18,2)),3)
else cast ( x/y as decimal(18,2)) end ReqOutput
This should do the trick...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
SomeNumber DECIMAL(9,7) NOT NULL
);
INSERT #TestData (SomeNumber) VALUES
(1.2345670), (1), (99.00100), (5.55);
SELECT
td.SomeNumber,
REVERSE(STUFF(rcv.RevCastVarchar, 1, PATINDEX('%[^0.]%', rcv.RevCastVarchar) - 1, ''))
FROM
#TestData td
CROSS APPLY ( VALUES (REVERSE(CAST(td.SomeNumber AS VARCHAR(10)))) ) rcv (RevCastVarchar);
HTH,
Jason