Extract last number out of existing one - sql

I have to extract the next number out of given numbers. My table contains numbers like below. The main product is always with .1 at the end and could or not contains his subproducts e.g:
07.0001.1 (main product)
07.0001.2 (his sub)
07.0001.3 (his sub)
etc..
01.1453.1
01.1453.2
03.3456.1
03.3456.2
03.3456.3
03.5436.1
03.5436.2
03.5436.3
03.5436.4
12.7839.1
12.7839.2
12.3232.1
12.4444.1
12.4444.2
13.7676.1
i want to pass first to digits of a number to the query and based on that get all which starts with that and then get the highest number out of next four and return this number + 1.
So if we would take above example inputs if i say 12 then it should find this product: 12.7839.x and return 12.7839 + 1 so 12.7840
Another example if i say 03 then should find 03.5436 so 03.5436 + 1 so should return 03.5437
Hope you know what i mean.
I am not so familiar with SQL but this is how far i am:
select * from tbArtikel where Nummer LIKE '12.%'

This is another alternate for achieving the desired results. Providing the option to pass number to be queried. Consider following SQL statements
CREATE TABLE tblDummyExample
(
Number VARCHAR(64)
)
INSERT INTO tblDummyExample
VALUES ('07.0001.1')
, ('07.0001.2')
, ('07.0001.3')
, ('01.1453.1')
, ('01.1453.2')
, ('03.3456.1')
, ('03.3456.2')
, ('03.3456.3')
, ('03.5436.1')
, ('03.5436.2')
, ('03.5436.3')
, ('03.5436.4')
, ('12.7839.1')
, ('12.7839.2')
, ('12.3232.1')
, ('12.4444.1')
, ('12.4444.2')
, ('13.7676.1')
DECLARE #startWith VARCHAR(2) = '12' -- provide any number as input
SELECT #startWith + '.'+ CAST((MAX(CAST(SUBSTRING(ex.Number, (CHARINDEX('.', ex.Number, 1) + 1), (CHARINDEX('.', ex.Number, (CHARINDEX('.', ex.Number, 1) + 1)) - (CHARINDEX('.', ex.Number, 1) + 1))) AS INT)) + 1) AS VARCHAR(16))
FROM tblDummyExample ex
WHERE ex.Number LIKE #startWith+'%'
I'm sure, this solution is not restricted to any specific SQL Server version.

Try this, extract the first two parts, convert the 2nd to a numeric value, add one and convert back to a string again:
select
parsename(max(nummer), 3) + '.' -- 03
+ ltrim(max(cast(parsename(nummer, 2) as int) +1)) -- 5436 -> 5437
+ '.1'
from tbArtikel
where Nummer LIKE '03.%'

Try like this,
DECLARE #table TABLE (col VARCHAR(10))
INSERT INTO #table
VALUES ('01.1453.1')
,('01.1453.2')
,('03.3456.1')
,('03.3456.2')
,('03.3456.3')
,('03.5436.1')
,('03.5436.2')
,('03.5436.3')
,('03.5436.4')
,('12.7839.1')
,('12.7839.2')
,('12.3232.1')
,('12.4444.1')
,('12.4444.2')
,('13.7676.1')
SELECT TOP 1 left(col, charindex('.', col, 1) - 1) + '.' + convert(VARCHAR(10), convert(INT, substring(col, charindex('.', col, 1) + 1, charindex('.', col, charindex('.', col, 1) + 1) - (charindex('.', col, 1) + 1))) + 1)
FROM #table
WHERE col LIKE '03.%'
ORDER BY 1 DESC

Related

SQL trying to replace middle characters with *

I am trying to replace SQL results with all the middle values with asterix, *. All results are words. I am using SSMS.
The words that are 4-5 letters, it should only show 1 letter in the beginning, one to the end.
6 letters and more, it it should only show 2 letter in the beginning, 2 letters in the end.
1-3 letters, no replacement.
For example:
(I am now using - instead of * so it does not make the text bold).
"Banana" 6 letters should become ba--na
"False" 5 letters should become F---e
"a" stays the same
"Selin is a vegetable and banana is a fruit" becomes "S---n is a ve-----le and ba--na is a f---t."
What I have done so far, is to make this for emails, after the #. But now I want it to happen with every word of the result.
What I've done:
DECLARE #String VARCHAR(100) = 'sample#gmail.com'
SELECT STUFF(STUFF(#STring,
CHARINDEX('#',#String)+2,
(CHARINDEX('.',#String, CHARINDEX('#',#String))-CHARINDEX('#',#String)-3),
REPLICATE('*',CHARINDEX('.',#String, CHARINDEX('#',#String))-CHARINDEX('#',#String)))
,2
,CHARINDEX('#',#String)-3
,REPLICATE('*',CHARINDEX('#',#String)-3))```
With result s----e#g------l.com
instead of -
And I tried the mask method
Select
--select first character from Email and use replicate
SUBSTRING(Sxolia,1,1) + REPLICATE('*',5)+
--function to put asterisks
SUBSTRING(Sxolia,CHARINDEX('#',Sxolia),len(Sxolia)-CHARINDEX('#',Sxolia)+1)
--at this statement i select this part #gmail,com and to first part to become like this A*****#gmail.com
as Emailmask
From [mytable]
With result
B***** Bana is a fruit
And
declare #str nvarchar(max)
select #str = '123456'
select '****' + substring(#str, 5, len(#str) - 3)
Result: ****56
Not what I am looking for.
How should I look into this?
If I had to deal with this in SQL Server I'd operate on each word as a row, however using string_split is not (currently) an option since it does not guarantee ordering.
The following uses json to split the string as an array and provides a key value for ordering, which allows the words to be aggregated in the correct order:
select t.Sentence,
String_Agg( masked, ' ') within group(order by seq) Masked
from t
cross apply (
select seq, [value] word,
case
when l<=3 then [value]
when l<=5 then Stuff([value],2,l-2,Replicate('*',l-2))
else
Stuff([value],3,l-4,Replicate('*',l-4))
end Masked
from (
select j.[value], 1 + Convert(tinyint,j.[key]) Seq
from OpenJson(Concat('["',replace(t.Sentence,' ', '","'),'"]')) j
)w
cross apply (values(Len([value])))x(l)
)w
group by t.Sentence;
See working demo
Result:
I'm not sure how e-mail fits into all this because you're asking for word masks, so I'm going to assume you actually want this. Use divide and conquer to implement this, so first implement an expression that would do this for simplest cases (e.g. single words). Then if you need it for e-mails, just split the e-mails however you see fit and then apply the same expression.
The expression itself is rather simple:
SELECT *
FROM (VALUES
('banana'),
('selin'),
('vegetable')
) words(word)
CROSS
APPLY (SELECT CASE
WHEN ln BETWEEN 4 AND 5
THEN LEFT(word, 1) + REPLICATE('*', ln-2) + RIGHT(word, 1)
WHEN ln >= 6
THEN LEFT(word, 2) + REPLICATE('*', ln-4) + RIGHT(word, 2)
ELSE word
END as result
FROM (VALUES (LEN(words.word))) x(ln)
) calc
This already provides the expected result. You could define a function out of this, if you have the permissions, and use it like so:
SELECT *
FROM (VALUES
('banana'),
('selin'),
('vegetable')
) words(word)
CROSS
APPLY fnMaskWord(word)
Here's a working demo on dbfiddle, it includes the statement to create the function.
Expanding on a few answers:
select case when len(#String) <= 3 then #String
when len(#String) > 3 AND len(#String) <= 5 then
substring(#String, 1, 2) +
REPLICATE('*', Len(#String) - 2) +
substring(#String, Len(#String) - 1, 2)
when len(#String) >= 6 then
substring(#String, 1, 2) +
REPLICATE('*', Len(#String) - 2) +
substring(#String, Len(#String) - 1, 2)
else 'unrecognized length!'
If the length of the string is less than or equal to 3, return the string.
If the length of the string is more than 3 and less than or equal to 5 then create a substring starting at position 1, then replicate * by the length of the string -2 and finally add another substring -1 from the end of the string.
Similar for if the result is over 6 characters.
Else unrecognized length!
Hope this helps understand what's going on!
Maybe this can help
declare #t table (word varchar(50))
insert into #t values ('banana'), ('selin'), ('vegetable')
select case when len(t.word) < 3 then t.word
else left(t.word, 1) + -- take first char from left
replicate('*', Len(t.word) - 2) + -- fill middle with *
right(t.word, 1) -- take last char from right
end
from #t t
this returns
COLUMN1
b****a
s***l
v*******e
If you want to keep 2 chars left and right when the len > 5 then maybe this
select case when len(t.word) < 3 then t.word
when len(t.word) < 6 then
left(t.word, 1) +
replicate('*', len(t.word) - 2) +
right(t.word, 1)
else left(t.word, 2) +
replicate('*', len(t.word) - 4) +
right(t.word, 2)
end
from #t t
The result
COLUMN1
ba**na
s***l
ve*****le
EDIT: What if there is a whole sentence ?
Well then we first split the sentence in words,
and then concat the individual words back together while putting the ** in them
declare #t table (word varchar(50))
insert into #t values ('banana'), ('selin'), ('vegetable'), ('Banana is a fruit')
select t.word,
-- put the words back togheter into the sentence, and ** them while we are at it
( select string_agg(case when len(value) < 3 then value
when len(value) < 6 then
left(value, 1) +
replicate('*', len(value) - 2) +
right(value, 1)
else left(value, 2) +
replicate('*', len(value) - 4) +
right(value, 2)
end,
' ')
)
from #t t
cross apply string_split(t.word, ' ') s -- split the sentence into words
group by t.word
the result is
word COLUMN1
---- -------
banana ba**na
Banana is a fruit Ba**na is a f***t
selin s***n
vegetable ve*****le

Best way to pad section of this string with 0s

This is 2 examples of what the string currently look like:
6731-121-1
9552-3-1
This is what I want to pad them to look like
0006731-121-1
0009552-003-1
So I want them to be padded with 7 zeroes before the first '-' then 3 zeroes between the first and second '-'
What would be the best way to accomplish this in SQL SELECT statement.
SELECT RIGHT('0000000'
+ ISNULL(
LEFT(OE.exception_id, CHARINDEX('-', OE.exception_id)
- 1) ,
''
) ,7) + '-'
+ SUBSTRING(OE.exception_id, CHARINDEX('-', ( OE.exception_id )), 10) exception_id
ParseName() could be an option here
Example
Declare #YourTable Table ([YourCol] varchar(50))
Insert Into #YourTable Values
('6731-121-1')
,('9552-3-1')
Select *
,NewVal = right('0000000'+parsename(replace(YourCol,'-','.'),3),7)
+'-'
+right('000'+parsename(replace(YourCol,'-','.'),2),3)
+'-'
+parsename(replace(YourCol,'-','.'),1)
From #YourTable
Returns
YourCol NewVal
6731-121-1 0006731-121-1
9552-3-1 0009552-003-1
In situations with more than 3 periods
Example: '1.2.3.4.5'
Or any value is empty
3 examples: '1..3', '1.2.3.', '.2'
Parsename will return null for all values. You will need to split the column using a different method.
Here is an alternative to parsename:
DECLARE #table table(col varchar(100))
INSERT #table values('6731-121-1'),('9552-3-1')
SELECT
col,
REPLICATE('0', 8-x) + STUFF(col, x+1, 0,REPLICATE('0', 4 - (y-x))) newcol
FROM #table
CROSS APPLY
(SELECT CHARINDEX('-', col) x) x
CROSS APPLY
(SELECT CHARINDEX('-', col + '-', x+1) y) y
col newcol
6731-121-1 0006731-121-1
9552-3-1 0009552-003-1

Pad Zero before first hypen and remove spaces and add BA and IN

I have data as below
98-45.3A-22
104-44.0A-23
00983-29.1-22
01757-42.5A-22
04968-37.3A2-23
Output Looking for output as below in SQL Server
00098-BA45.3A-IN-22
00104-BA44.0A-IN-23
00983-BA29.1-IN-22
01757-BA42.5A-IN-22
04968-BA37.3A2-IN-23
I splitted parts to cope with tricky data templates. This should work even with non-dash-2-digit tail:
WITH Src AS
(
SELECT * FROM (VALUES
('98-45.3A-22'),
('104-44.0A-23'),
('00983-29.1-22'),
('01757-42.5A-22'),
('04968-37.3A2-23')
) T(X)
), Parts AS
(
SELECT *,
RIGHT('00000'+SUBSTRING(X, 1, CHARINDEX('-',X, 1)-1),5) Front,
'BA'+SUBSTRING(X, CHARINDEX('-',X, 1)+1, 2) BA,
SUBSTRING(X, PATINDEX('%.%',X), LEN(X)-CHARINDEX('-', REVERSE(X), 1)-PATINDEX('%.%',X)+1) P,
SUBSTRING(X, LEN(X)-CHARINDEX('-', REVERSE(X), 1)+1, LEN(X)) En
FROM Src
)
SELECT Front+'-'+BA+P+'-IN'+En
FROM Parts
It returns:
00098-BA45.3A-IN-22
00104-BA44.0A-IN-23
00983-BA29.1-IN-22
01757-BA42.5A-IN-22
04968-BA37.3A2-IN-23
Try this,
DECLARE #String VARCHAR(100) = '98-45.3A-22'
SELECT ISNULL(REPLICATE('0',6 - CHARINDEX('-',#String)),'') -- Add leading Zeros
+ STUFF(
STUFF(#String,CHARINDEX('-',#String),1,'-BA'), -- Add 'BA'
CHARINDEX('-',#String,CHARINDEX('-',#String)+1)+2, -- 2 additional for the character 'BA'
1,'-IN') -- Add 'IN'
What if I have more than 6 digit number before first hyphen and want to remove the leading zeros to make it 6 digits.
DECLARE #String VARCHAR(100) = '0000098-45.3A-22'
SELECT CASE WHEN CHARINDEX('-',#String) <= 6
THEN ISNULL(REPLICATE('0',6 - CHARINDEX('-',#String)),'') -- Add leading Zeros
+ STUFF(
STUFF( #String,CHARINDEX('-',#String),1,'-BA'), -- Add 'BA'
CHARINDEX('-',#String,CHARINDEX('-',#String)+1)+2, -- 2 additional for the character 'BA'
1,'-IN') -- Add 'IN'
ELSE STUFF(
STUFF(
STUFF(#String,CHARINDEX('-',#String),1,'-BA'), -- Add 'BA'
CHARINDEX('-',#String,CHARINDEX('-',#String)+1)+2, -- 2 additional for the character 'BA'
1,'-IN'), -- Add 'IN'
1, CHARINDEX('-',#String) - 6, '' -- remove extra leading Zeros
)
END
Making assumptions that the format is consistent (e.g. always ends with "-" + 2 characters....)
DECLARE #Data TABLE (Col1 VARCHAR(100))
INSERT #Data ( Col1 )
SELECT Col1
FROM (
VALUES ('98-45.3A-22'), ('104-44.0A-23'),
('00983-29.1-22'), ('01757-42.5A-22'),
('04968-37.3A2-23')
) x (Col1)
SELECT RIGHT('0000' + LEFT(Col1, CHARINDEX('-', Col1) - 1), 5)
+ '-BA' + SUBSTRING(Col1, CHARINDEX('-', Col1) + 1, CHARINDEX('.', Col1) - CHARINDEX('-', Col1))
+ SUBSTRING(Col1, CHARINDEX('.', Col1) + 1, LEN(Col1) - CHARINDEX('.', Col1) - 3)
+ '-IN-' + RIGHT(Col1, 2)
FROM #Data
It's not ideal IMO to do this string manipulation all the time in SQL. You could shift it out to your presentation layer, or store the pre-formatted value in the db to save the cost of this every time.
Use REPLICATE AND CHARINDEX:
Replicate: will repeat given character till reach required count specify in function
CharIndex: Finds the first occurrence of any character
Declare #Data AS VARCHAR(50)='98-45.3A-22'
SELECT REPLICATE('0',6-CHARINDEX('-',#Data)) + #Data
SELECT
SUBSTRING
(
(REPLICATE('0',6-CHARINDEX('-',#Data)) +#Data)
,0
,6
)
+'-'+'BA'+ CAST('<x>' + REPLACE(#Data,'-','</x><x>') + '</x>' AS XML).value('/x[2]','varchar(max)')
+'-'+ 'IN'+ '-' + CAST('<x>' + REPLACE(#Data,'-','</x><x>') + '</x>' AS XML).value('/x[3]','varchar(max)')
In another way by using PARSENAME() you can use this query:
WITH t AS (
SELECT
PARSENAME(REPLACE(REPLACE(s, '.', '###'), '-', '.'), 3) AS p1,
REPLACE(PARSENAME(REPLACE(REPLACE(s, '.', '###'), '-', '.'), 2), '###', '.') AS p2,
PARSENAME(REPLACE(REPLACE(s, '.', '###'), '-', '.'), 1) AS p3
FROM yourTable)
SELECT RIGHT('00000' + p1, 5) + '-BA' + p2 + '-IN-' + p3
FROM t;

Extract string between after second / and before -

I have a field that holds an account code. I've managed to extract the first 2 parts OK but I'm struggling with the last 2.
The field data is as follows:
812330/50110/0-0
812330/50110/BDG001-0
812330/50110/0-X001
I need to get the string between the second "/" and the "-" and after the "-" .Both fields have variable lengths, so I would be looking to output 0 and 0 on the first record, BDG001 and 0 on the second record and 0 and X001 on the third record.
Any help much appreciated, thanks.
You can use CHARINDEX and LEFT/RIGHT:
CREATE TABLE #tab(col VARCHAR(1000));
INSERT INTO #tab VALUES ('812330/50110/0-0'),('812330/50110/BDG001-0'),
('812330/50110/0-X001');
WITH cte AS
(
SELECT
col,
r = RIGHT(col, CHARINDEX('/', REVERSE(col))-1)
FROM #tab
)
SELECT col,
r,
sub1 = LEFT(r, CHARINDEX('-', r)-1),
sub2 = RIGHT(r, LEN(r) - CHARINDEX('-', r))
FROM cte;
LiveDemo
EDIT:
or even simpler:
SELECT
col
,sub1 = SUBSTRING(col,
LEN(col) - CHARINDEX('/', REVERSE(col)) + 2,
CHARINDEX('/', REVERSE(col)) -CHARINDEX('-', REVERSE(col))-1)
,sub2 = RIGHT(col, CHARINDEX('-', REVERSE(col))-1)
FROM #tab;
LiveDemo2
EDIT 2:
Using PARSENAME SQL SERVER 2012+ (if your data does not contain .):
SELECT
col,
sub1 = PARSENAME(REPLACE(REPLACE(col, '/', '.'), '-', '.'), 2),
sub2 = PARSENAME(REPLACE(REPLACE(col, '/', '.'), '-', '.'), 1)
FROM #tab;
LiveDemo3
...Or you can do this, so you only go from left side to right, so you don't need to count from the end in case you have more '/' or '-' signs:
SELECT
SUBSTRING(columnName, CHARINDEX('/' , columnName, CHARINDEX('/' , columnName) + 1) + 1,
CHARINDEX('-', columnName) - CHARINDEX('/' , columnName, CHARINDEX('/' , columnName) + 1) - 1) AS FirstPart,
SUBSTRING(columnName, CHARINDEX('-' , columnName) + 1, LEN(columnName)) AS LastPart
FROM table_name
One method way is to download a split() function off the web and use it. However, the values end up in separate rows, not separate columns. An alternative is a series of nested subqueries, CTEs, or outer applies:
select t.*, p1.part1, p12.part2, p12.part3
from table t outer apply
(select t.*,
left(t.field, charindex('/', t.field)) as part1,
substring(t.field, charindex('/', t.field) + 1) as rest1
) p1 outer apply
(select left(p1.rest1, charindex('/', p1.rest1) as part2,
substring(p1.rest1, charindex('/', p1.rest1) + 1, len(p1.rest1)) as part3
) p12
where t.field like '%/%/%';
The where clause guarantees that the field value is in the right format. Otherwise, you need to start sprinkling the code with case statements to handle misformated data.

SQL Query to parse numbers from name

The DBMS in this case is SQL Server 2012.
I need a SQL query that will grab just the numbers from a device name. I've got devices that follow a naming scheme that SHOULD look like this:
XXXnnnnn
or
XXXnnnnn-XX
Where X is a letter and n is a number which should be left padded with 0's where appropriate. However, not all of the names are properly padded in this way.
So, imagine you have a column that looks something like this:
Name
----
XXX01234
XXX222
XXX0390-A2
XXX00965-A1
I need an SQL query that will return results from this example column as follows.
Number
------
01234
00222
00390
00965
Anyone have any thoughts? I've tried things like casting the name first as a float and then as an int, but to be honest, I'm just not skilled enough with SQL yet to find the solution.
Any help is greatly appreciated!
SQL Server does not have great string parsing functions. For your particular example, I think a case statement might be the simplest approach:
select (case when number like '___[0-9][0-9][0-9][0-9][0-9]%'
then substring(number, 4, 5)
when number like '___[0-9][0-9][0-9][0-9]%'
then '0' + substring(number, 4, 4)
when number like '___[0-9][0-9][0-9]%'
then '00' + substring(number, 4)
when number like '___[0-9][0-9]%'
then '000' + substring(number, 4, 2)
when number like '___[0-9][0-9]%'
then '0000' + substring(number, 4, 1)
else '00000'
end) as EmbeddedNumber
This might work :
SELECT RIGHT('00000'
+ SUBSTRING(Col, 1, ISNULL(NULLIF((PATINDEX('%-%', Col)), 0) - 1, LEN(Col))), 5)
FROM (SELECT REPLACE(YourColumn, 'XXX', '') Col
FROM YourTable)t
SQLFIDDLE
This will work even when XXX can be of different len:
DECLARE #t TABLE ( n NVARCHAR(50) )
INSERT INTO #t
VALUES ( 'XXXXXXX01234' ),
( 'XX222' ),
( 'X0390-A2' ),
( 'XXXXXXX00965-A1' )
SELECT REPLICATE('0', 5 - LEN(n)) + n AS n
FROM ( SELECT SUBSTRING(n, PATINDEX('%[0-9]%', n),
CHARINDEX('-', n + '-') - PATINDEX('%[0-9]%', n)) AS n
FROM #t
) t
Output:
n
01234
00222
00390
00965
If the first 3 chars are always needed to be removed, then you can do something like that (will work if the characters will start only after '-' sign):
DECLARE #a AS TABLE ( a VARCHAR(100) );
INSERT INTO #a
VALUES
( 'XXX01234' ),
( 'XXX222' ),
( 'XXX0390-A2' ),
( 'XXX00965-A1' );
SELECT RIGHT('00000' + SUBSTRING(a, 4, CHARINDEX('-',a+'-')-4),5)
FROM #a
-- OUTPUT
01234
00222
00390
00965
Another option (will extract numbers after first 3 characters):
SELECT
RIGHT('00000' + LEFT(REPLACE(a, LEFT(a, 3), ''),
COALESCE(NULLIF(PATINDEX('%[^0-9]%',
REPLACE(a, LEFT(a, 3), '')),
0) - 1,
LEN(REPLACE(a, LEFT(a, 3), '')))), 5)
FROM
#a;
-- OUTPUT
01234
00222
00390
00965