For a problem like this you want to avoid using a function (unless an iTVF) and certainly avoid using a loop, as neither perform very well.
This might not be the compact way to go about it, but the logic is clearer (IMO). You could move this into a function (iTVF) for reusability.
- Find the string "APP" and remove it and all preceding text.
- Remove any non-numeric characters immediately following "APP"
- Obtain the number using
substring from here to the next space.
with TestValues as (
select *
from (
values
('Data Service B2B APP#1234 Rehearsal 03/01/2025'),
('Office 365 APP 23456 for project 123'),
('Office 365 APP555 for project 123')
) x (Value)
), cte1 as (
-- Find "APP" and remove from string
select Value
, substring(Value, patindex('%APP%', Value) + 3, len(Value)) NewValue
from TestValues
), cte2 as (
select Value
-- Find first numeric value after "APP"
, substring(NewValue, patindex('%[0-9]%', NewValue), len(NewValue)) NewNewValue
from cte1
)
select Value
-- Take a string until the next space
, substring(NewNewValue, 1, patindex('% %',NewNewValue)) Number
from cte2;
Returns
| Value |
Number |
| Data Service B2B APP#1234 Rehearsal 03/01/2025 |
1234 |
| Office 365 APP 23456 for project 123 |
23456 |
| Office 365 APP555 for project 123 |
555 |
fiddle
A tidier solution is (Thanks for the reminder Martin Smith):
select Value, number
from (
values
('Data Service B2B APP#1234 Rehearsal 03/01/2025'),
('Office 365 APP 23456 for project 123'),
('Office 365 APP555 for project 123')
) TestValues (Value)
cross apply (values(substring(Value, patindex('%APP%', Value) + 3, len(Value)))) ca1 (substring_after_app)
cross apply (values(substring(substring_after_app, patindex('%[0-9]%', substring_after_app), len(substring_after_app)))) ca2 (substring_from_first_digit)
cross apply (values(substring(substring_from_first_digit, 1, patindex('% %',substring_from_first_digit)))) ca3 (number)
To run this against your own table use the following:
select Value, number
from MyTable
cross apply (values(substring(Value, patindex('%APP%', Value) + 3, len(Value)))) ca1 (substring_after_app)
cross apply (values(substring(substring_after_app, patindex('%[0-9]%', substring_after_app), len(substring_after_app)))) ca2 (substring_from_first_digit)
cross apply (values(substring(substring_from_first_digit, 1, patindex('% %',substring_from_first_digit)))) ca3 (number)
WHILEwould likely be one of the slowest ways to do this. An iTVF would be a far better option.23456is not "before the next space after APP*". Please clarify when and why spaces should be ignored.