Executing SP inside while loop works only first time - sql-server

In SQL Server 2012, I have a script that call a stored procedure multiple times.. The loop will be executed as many times as the number of records in [ResultHeader] table.
EXEC [AS400].tp_SelectChildItems #Pgm, #Grp, #Pgmgrpseq,
null, null, null, null, null, #ItemSelection, null,
null, null, null, null,
Note: This SP has a table variable inside
I have a comma separated list of SelectedCostPageList. For each value in this list, the loop is executed once.
I am observing a weird behavior. When I invoke the sp from the script, only for the first time the sp is returning proper records.. On subsequent calls the stored procedure is returning no records. Very odd…. When I change the order of comma separated string, only for the first iteration the sp is returing records.. other times no records.
Any guess what will be the reason?
DECLARE #SelectedCostPageList VARCHAR(MAX)
SET #SelectedCostPageList = (SELECT (SELECT CONVERT(VARCHAR(32), A.PGM + '-'+A.GRP+'-'+A.PGMGRPSEQ) + ', ' AS [text()]
FROM dbo.[ResultHeader] A
WHERE UserId = 'U25703'
FOR XML PATH('')) AS CostPages) ResultHeader
PRINT #SelectedCostPageList
DECLARE #pos INT
DECLARE #len INT
set #pos = 0
set #len = 0
DELETE FROM ResultItems
WHILE CHARINDEX(',', #SelectedCostPageList, #pos+1)>0
BEGIN
set #len = CHARINDEX(',', #SelectedCostPageList, #pos+1) - #pos
DECLARE #SelectedCostPage VARCHAR(10)
set #SelectedCostPage = SUBSTRING(#SelectedCostPageList, #pos, #len)
set #pos = CHARINDEX(',', #SelectedCostPageList, #pos+#len) +1
PRINT #SelectedCostPage
DECLARE #Pgm AS VARCHAR (10);
DECLARE #Grp AS VARCHAR (10);
DECLARE #Pgmgrpseq AS VARCHAR (10);
SELECT #Pgm = (SELECT VALUE
FROM [AS400].[tf_SelectSplitString] (#SelectedCostPage, '-')
WHERE POSITION = 1);
PRINT #Pgm
SELECT #Grp = (SELECT VALUE
FROM [AS400].[tf_SelectSplitString] (#SelectedCostPage, '-')
WHERE POSITION = 2);
PRINT #Grp
SELECT #Pgmgrpseq = (SELECT VALUE
FROM [AS400].[tf_SelectSplitString] (#SelectedCostPage, '-')
WHERE POSITION = 3);
PRINT #Pgmgrpseq
Declare #ItemSelection as varchar(4000)
set #ItemSelection ='1116102540,1116102541';
print #ItemSelection
print 'HAI'
print #Pgmgrpseq
EXEC [AS400].tp_SelectChildItems
#Pgm, #Grp, #Pgmgrpseq,
null, null, null,
null, null, #ItemSelection, null,
null, null, null, null,
null, null, null, 1, 'U25703'
END
SELECT * FROM ResultItems
WHERE UserId = 'U25703'

If I had to guess, it would be because you are using ', ' (comma followed by a space) as a list separator, but the code is just looking for a comma. You would then have a leading space on a string value, which, presumably, would not then match whatever needs to be matched.
The first works, because you initialize the list string with a comma without a space.
Perhaps more importantly, I find the approach of stuffing things into a string to loop over them to be, shall I say, very 1980s. You could use a cursor (not my favorite approach) or a temporary table. When I want to do loops over such data, I sometimes structure them as follows:
declare #looptable table (rownum int identity(1, 1), . . . );
insert into #looptable . . .;
declare #tot int;
select #tot = count(*) from #looptable;
declare #i int = 1;
while (#i <= tot)
begin
-- something here with "where rownum = #i"
end;
The idea of stuffing values into a string just to write more code to parse them out seems like a waste of programming effort. In addition, this can cause problems because of the conversion of types into character formats and the obscene ways that for path xml handles characters such as '<' and '>'.

Related

Split address name from number SQL server [duplicate]

I am trying to parse out a value from a string that involves getting the last index of a string. Currently, I am doing a horrible hack that involves reversing a string:
SELECT REVERSE(SUBSTRING(REVERSE(DB_NAME()), 1,
CHARINDEX('_', REVERSE(DB_NAME()), 1) - 1))
To me this code is nearly unreadable. I just upgraded to SQL Server 2016 and I hoping there is a better way.
Is there?
If you want everything after the last _, then use:
select right(db_name(), charindex('_', reverse(db_name()) + '_') - 1)
If you want everything before, then use left():
select left(db_name(), len(db_name()) - charindex('_', reverse(db_name()) + '_'))
Wrote 2 functions, 1 to return LastIndexOf for the selected character.
CREATE FUNCTION dbo.LastIndexOf(#source nvarchar(80), #pattern char)
RETURNS int
BEGIN
RETURN (LEN(#source)) - CHARINDEX(#pattern, REVERSE(#source))
END;
GO
and 1 to return a string before this LastIndexOf. Maybe it will be useful to someone.
CREATE FUNCTION dbo.StringBeforeLastIndex(#source nvarchar(80), #pattern char)
RETURNS nvarchar(80)
BEGIN
DECLARE #lastIndex int
SET #lastIndex = (LEN(#source)) - CHARINDEX(#pattern, REVERSE(#source))
RETURN SUBSTRING(#source, 0, #lastindex + 1)
-- +1 because index starts at 0, but length at 1, so to get up to 11th index, we need LENGTH 11+1=12
END;
GO
No, SQL server doesnt have LastIndexOf.
This are the available string functions
But you can always can create your own function
CREATE FUNCTION dbo.LastIndexOf(#source text, #pattern char)
RETURNS
AS
BEGIN
DECLARE #ret text;
SELECT into #ret
REVERSE(SUBSTRING(REVERSE(#source), 1,
CHARINDEX(#pattern, REVERSE(#source), 1) - 1))
RETURN #ret;
END;
GO
Once you have one of the split strings from here,you can do it in a set based way like this..
declare #string varchar(max)
set #string='C:\Program Files\Microsoft SQL Server\MSSQL\DATA\AdventureWorks_Data.mdf'
;with cte
as
(select *,row_number() over (order by (select null)) as rownum
from [dbo].[SplitStrings_Numbers](#string,'\')
)
select top 1 item from cte order by rownum desc
**Output:**
AdventureWorks_Data.mdf
CREATE FUNCTION dbo.LastIndexOf(#text NTEXT, #delimiter NTEXT)
RETURNS INT
AS
BEGIN
IF (#text IS NULL) RETURN NULL;
IF (#delimiter IS NULL) RETURN NULL;
DECLARE #Text2 AS NVARCHAR(MAX) = #text;
DECLARE #Delimiter2 AS NVARCHAR(MAX) = #delimiter;
DECLARE #Index AS INT = CHARINDEX(REVERSE(#Delimiter2), REVERSE(#Text2));
IF (#Index < 1) RETURN 0;
DECLARE #ContentLength AS INT = (LEN('|' + #Text2 + '|') - 2);
DECLARE #DelimiterLength AS INT = (LEN('|' + #Delimiter2 + '|') - 2);
DECLARE #Result AS INT = (#ContentLength - #Index - #DelimiterLength + 2);
RETURN #Result;
END
Allows for multi-character delimiters like ", " (comma space).
Returns 0 if the delimiter is not found.
Takes a NTEXT for comfort reasons as NVARCHAR(MAX)s are implicitely cast into NTEXT but not vice-versa.
Handles delimiters with leading or tailing space correctly!
Try:
select LEN('tran van abc') + 1 - CHARINDEX(' ', REVERSE('tran van abc'))
So, the last index of ' ' is : 9
I came across this thread while searching for a solution to my similar problem which had the exact same requirement but was for a different kind of database that was lacking the REVERSE function.
In my case this was for a OpenEdge (Progress) database, which has a slightly different syntax. This made the INSTR function available to me that most Oracle typed databases offer.
So I came up with the following code:
SELECT
INSTR(foo.filepath, '/',1, LENGTH(foo.filepath) - LENGTH( REPLACE( foo.filepath, '/', ''))) AS IndexOfLastSlash
FROM foo
However, for my specific situation (being the OpenEdge (Progress) database) this did not result into the desired behaviour because replacing the character with an empty char gave the same length as the original string. This doesn't make much sense to me but I was able to bypass the problem with the code below:
SELECT
INSTR(foo.filepath, '/',1, LENGTH( REPLACE( foo.filepath, '/', 'XX')) - LENGTH(foo.filepath)) AS IndexOfLastSlash
FROM foo
Now I understand that this code won't solve the problem for T-SQL because there is no alternative to the INSTR function that offers the Occurence property.
Just to be thorough I'll add the code needed to create this scalar function so it can be used the same way like I did in the above examples. And will do exactly what the OP wanted, serve as a LastIndexOf method for SQL Server.
-- Drop the function if it already exists
IF OBJECT_ID('INSTR', 'FN') IS NOT NULL
DROP FUNCTION INSTR
GO
-- User-defined function to implement Oracle INSTR in SQL Server
CREATE FUNCTION INSTR (#str VARCHAR(8000), #substr VARCHAR(255), #start INT, #occurrence INT)
RETURNS INT
AS
BEGIN
DECLARE #found INT = #occurrence,
#pos INT = #start;
WHILE 1=1
BEGIN
-- Find the next occurrence
SET #pos = CHARINDEX(#substr, #str, #pos);
-- Nothing found
IF #pos IS NULL OR #pos = 0
RETURN #pos;
-- The required occurrence found
IF #found = 1
BREAK;
-- Prepare to find another one occurrence
SET #found = #found - 1;
SET #pos = #pos + 1;
END
RETURN #pos;
END
GO
To avoid the obvious, when the REVERSE function is available you do not need to create this scalar function and you can just get the required result like this:
SELECT
LEN(foo.filepath) - CHARINDEX('\', REVERSE(foo.filepath))+1 AS LastIndexOfSlash
FROM foo
Try this.
drop table #temp
declare #brokername1 nvarchar(max)='indiabullssecurities,canmoney,indianivesh,acumencapitalmarket,sharekhan,edelweisscapital';
Create Table #temp
(
ID int identity(1,1) not null,
value varchar(100) not null
)
INSERT INTO #temp(value) SELECT value from STRING_SPLIT(#brokername1,',')
declare #id int;
set #id=(select max(id) from #temp)
--print #id
declare #results varchar(500)
select #results = coalesce(#results + ',', '') + convert(varchar(12),value)
from #temp where id<#id
order by id
print #results

Replacing individual digits in a CustomerID field SQL Server

I have some customer data that needs to be anonymised. I have customerIds which consists of numbers.
for example:
CustomerID
3937487
I need to swap each digit with an alternative, which should be enough for my requirement. Based on the following lookup table
Only issue I'm having is when I use the REPLACE function on the field:
REPLACE(REPLACE(CustomerID,2,9),9,6)
which gives me
CustomerID
3637487
It's swapping the digit 2 to a 9, then that same 9 to a 6. It needs to only replace the digits ONCE.
As I'm going to be changing millions of records in one go, using temp tables isn't possible from a performance perspective. Can this be done in one query, recursively?
I can't think of any way of accomplishing this in a single query. If I wanted to do this I'd create a function something along the lines of
CREATE FUNCTION [dbo].[AnonymiseId]
(
#Id [int]
)
RETURNS [int]
AS
BEGIN
-- Declare the return variable here
DECLARE #ResultVar int;
DECLARE #substitutions nvarchar(10) = '7295380146';
DECLARE #stringId nvarchar(100) = CONVERT(nvarchar(100), #Id);
DECLARE #i int = 1
DECLARE #substituteStringId nvarchar(100) = '';
WHILE #i <= LEN(#stringID)
BEGIN
DECLARE #char nvarchar = SUBSTRING(#stringId, #i, 1);
DECLARE #charValue int = CONVERT(int, #char);
DECLARE #subsChar nvarchar = SUBSTRING(#substitutions, #charValue + 1, 1);
SET #substituteStringId = CONCAT(#substituteStringId, #subsChar);
SET #i = #i + 1
END
SET #ResultVar = CONVERT(int, #substituteStringId);
-- Return the result of the function
RETURN #ResultVar;
END
GO
and then just use it in the query
SELECT dbo.AnonymiseId(CustomerID) FROM ???

Generate a string format based on fixed length and numeric units on each increment

I had to write a SP to generate a string with the combination of Country (7 chars) + Province (1 char) + numeric number starts from 01. for eg: JAMAICAK01
where JAMAICA (Country) , K (Province) and 01 is the numeric number that gets incremented by 1 for each transaction.
The issue I have here is the generated string length max is 10, can be lesser than 10 but not >10.
It should be handled in a way with certain rules like
the combination don't exist
When the numeric unit changes from tens to hundreds making the string
length >10, I need to remove the right chars for eg JAMAICAKKK10 to
JAMAICAK10 from the right to make the total max length 10.
In my code below I tried to check if the combination exists and I get the max of it and do the numeric increment from the last one. think it can be done in a better way.
Declare #Province char(2)
Declare #Country varchar(10)
declare #CounProv varchar(10)
Declare #SimilarCounPRov varchar(max) = '';
declare #FinalString nvarchar(12)
declare #s varchar(50)
declare #s1 varchar(50)
declare #s2 varchar(50)
Set #Province = LEFT('KINGSTON', 1)
Set #Country = LEFT('JAMAICA', 7)
Set #CounProv = #Country+#Province
Select #SimilarCounPRov = MAX(field1) from dbo.table where field1
LIKE '%JAMAICAK%'
if #SimilarCounPRov IS NOT NULL
BEGIN
Set #s = (select fn_AlphaOnly('%JAMAICAK99%')) -- returns JAMAICAK
Set #s1 = (select fn_NumericOnly('%JAMAICAK99%')) -- returns 199
set #s2= #s1 +1 -- increment by 1
if len(#FinalString) > 10
----
need help here----`
I'm not sure that I understood all your requirements but if you need to generate strings like : JAMAICAK1,JAMAICAK2,...JAMAICAK10...,JAMAICAK11,...JAMAICA100,JAMAICA101,...JAMAIC1000,JAMAIC1001...
You can try to exploit this piece of code :
Declare #Province char(2)
Declare #Country varchar(10)
Declare #CounProv varchar(10)
Declare #value int
Declare #str_value VARCHAR(100)
Set #Province = LEFT('KINGSTON', 1)
Set #Country = LEFT('JAMAICA', 7)
Set #value = 999999
Set #CounProv = #Country+#Province
Set #str_value = (select CAST(#value AS varchar(100)))
select LEFT(#CounProv,10-LEN(#str_value))+#str_value
Tell me if it helps.

Can anyone identify why my loop isn't working correctly?

I am trying to do an insert, so that when a clientID value is NULL then insert the client data into the client table.
I have a loop that cycles through data entered into a temp table, when the clientID is null it should do an insert and choose the next sequential client reference for that client then delete the row from the temp table and move onto the next.
The problem is when the loop does the second insert or more, it is using the SAME client reference even though I have specified +1. The below is an extract of the loop, can anyone figure out why after the first insert the client reference stays the same? If I run the insert by itself with the loop and select #result it shows the vaues sequentially so I don't understand why when the script runs it doesn't insert the reference sequentially.
Declare #Id int
WHILE EXISTS(SELECT * FROM #Temp)
begin
select top 1 #id = ID from #temp
IF (select clientID from #m1 where id = #id) is null AND (select renewalinsuredid from #m1 where id=#id) is not null and (select renewalmasterID from #m1 where id=#id) is not null
BEGIN
declare #result varchar(10)
SELECT #Result = (MAX(CAST(SUBSTRING(ClientReference1,3,6) AS INTEGER)) + 1) FROM Client
set #result = 'CR0' + #result
INSERT INTO Client (clientid,InsuredName,InsuredId,MasterInsuredId,ClientReference1)
SELECT newid(),insuredname,RenewalInsuredID,RenewalMasterID,#result from #M1 where id = #id
PRINT 'Client ref '+ cast(#result as varchar(64)) +' inserted for policy ' + #result2
END
DELETE from #temp where ID = #Id
END
you do...
SELECT #Result = (MAX(CAST(SUBSTRING(ClientReference1,3,6) AS INTEGER)) + 1) FROM Client
...to get the maximum client reference CAST to an integer, then add 1 to it - this doesn't guarantee that #Result is 'greater' than all the other ClientReference1, because ClientReference1 appears to be a text field - consider a field '9', cast it to integer, and add 1 - what have you got? A 10 - if I change back to text, then '10' < '9' in terms of alphanumerics
interchanging from numeric to string, and sorting numerics that have changed to strings, can have unwanted effects. Also you add'CR0' to the start of the string, that could confuse things possibly.
So I managed to get it to work after converting the value. It seems to be due to the datatype being int as when converted to varchar the insert works correctly. The main thing that I don't understand is how if i just did select #result to see what the output was - it was correct every time, just didn't seem to insert the value correctly. Thanks for the help people.
declare #result varchar(10)
declare #Length int
declare #Refresult varchar(10)
SELECT #Result = (MAX(CAST(SUBSTRING(ClientReference1,3,6) AS INTEGER)) + 1) FROM Client
SET #Length = CONVERT(INT,#Result)
SET #Result = CONVERT(VARCHAR(10),#Length)
SET #Length = LEN(#Result)
IF(#Length =5)
SET #Result = '0' + #Result
IF #Result IS NULL
BEGIN
SET #Result = '00000' + '1'
END
SET #Refresult = 'CR' + #Result

Reset the ID counter on a stored procedure in SQL Server

I'm developing a system that manages work orders for vehicles. The ID of work orders is composed as follows: OT-001-16.
Where OT- is a string, 001 is the counter, followed by - character and finally the number 16 is the current year.
Example:
If the current year is 2018, the ID should be OT-001-18.
The problem is when the year changes, the counter must restart from 001. I have a stored procedure to do that, but i think i'm doing a lot more work.
This is my stored procedure code:
CREATE PROCEDURE ot (#name varchar(100), #area varchar(100), #idate varchar(100), #edate varchar(100))
AS
BEGIN
SET NOCOUNT ON;
DECLARE #aux varchar(100);
DECLARE #aux2 varchar(100);
DECLARE #aux3 int;
DECLARE #aux4 varchar(100);
SELECT #aux = id_workorder FROM idaux;
IF (#aux IS NULL)
SET #aux = CONCAT('OT-000-', RIGHT(YEAR(GETDATE()), 2));
SET
#aux2 = SUBSTRING(
#aux, CHARINDEX('-', #aux) + 1,
LEN(#aux) - CHARINDEX('-', #aux) - CHARINDEX('-', REVERSE(#aux)));
SET #aux3 = CAST(#aux2 AS int) + 1;
SET #aux4 = #aux3;
IF #aux3 < 1000
IF #aux3 >= 10
SET #aux4 = CONCAT('0', #aux4);
ELSE
SET #aux4 = CONCAT('00', #aux4);
ELSE
SET #aux4 = #aux4;
DECLARE #f varchar(100);
DECLARE #y varchar(50);
SELECT TOP 1
#y = id_workorder
FROM workorder
WHERE (RIGHT(id_workorder, 2)) = (RIGHT(YEAR(GETDATE()), 2))
ORDER BY id_workorder DESC;
DECLARE #yy varchar(10);
SET
#yy = RIGHT(#y, 2);
DECLARE #yn varchar(10);
SET
#yn = RIGHT(YEAR(GETDATE()), 2);
BEGIN
IF #yn = #yy
BEGIN
DECLARE #laux varchar(20)
SET #f = 'OT-' + #aux4 + '-' + RIGHT(YEAR(GETDATE()), 2);
INSERT INTO workorder (id_workorder, name, area, initial_date, end_date)
VALUES (#f, #name, #area, #idate, #edate);
SELECT
#laux = id_workorder
FROM idaux
IF (#laux IS NULL)
BEGIN
INSERT idaux (id_workorder) VALUES (#f);
END
ELSE
BEGIN
UPDATE idaux SET id_workorder = #f;
END
END
ELSE
BEGIN
SET #f = CONCAT('OT-001-', (RIGHT(YEAR(GETDATE()), 2)));
INSERT INTO workorder (id_workorder, name, area, initial_date, end_date)
VALUES (#f, #name, #area, #idate, #edate);
SELECT #laux = id_workorder FROM idaux;
IF (#laux IS NULL)
BEGIN
INSERT idaux (id_workorder) VALUES (#f);
END
ELSE
BEGIN
UPDATE idaux SET id_workorder = #f;
END
END
END
END
Basically, i created an auxiliar table to save the last Work Order ID, then from this table called idaux i take the ID and i compared to new possible ID by a string handling. Then if the year of the last ID saved are equal to the current year the counter increases, but if not the counter is restarted to 001, the new ID is updated in the auxiliar table and the Work Order is inserted to the table workorder.
My stored procedure works, but i need your help to optimize the stored procedure. Any question post on comments.
Here is how I'd setup the stored procedure and the underlying table to keep track of your work orders:
create database tmpWorkOrders;
go
use tmpWorkOrders;
go
/*
The work order ID (as you wish to see it) and the
work order counter (per year) will be separated into
two separate columns (with a unique constraint).
The work order ID (you wish to see) is automatically
generated for you and stored "persisted":
http://stackoverflow.com/questions/916068/sql-server-2005-computed-column-is-persisted
*/
create table WorkOrders
(
SurrogateKey int identity(1, 1) primary key not null,
WorkOrderYear int not null,
WorkOrderCounter int not null,
WorkOrderID as
N'OT-' + right(N'000' + cast(WorkOrderCounter as nvarchar), 3)
+ N'-' + right(cast(WorkOrderYear as nvarchar), 2)persisted,
WorkOrderDescription nvarchar(200),
constraint UQ_WorkOrderIDs
unique nonclustered (WorkOrderYear, WorkOrderCounter)
);
go
create procedure newWorkOrder
(#WorkOrderYear int = null,
#WorkOderCounter int = null,
#WorkOrderDescription nvarchar(200) = null
)
as
begin
/*
If no year is given the the current year is assumed
*/
if #WorkOrderYear is null
begin
set #WorkOrderYear = year(current_timestamp);
end;
/*
If no work order counter (for the above year) is given
then the next available one will be given
*/
if #WorkOderCounter is null
begin
set #WorkOderCounter
= isnull(
(
select max(WorkOrderCounter)
from WorkOrders
where WorkOrderYear = #WorkOrderYear
) + 1,
0
);
end;
else
/*
If a work order counter has been passed to the
stored procedure then it must be validated first
*/
begin
/*
Does the work order counter (for the given year)
already exist?
*/
if exists
(
select 1
from dbo.WorkOrders as wo
where wo.WorkOrderYear = #WorkOrderYear
and wo.WorkOrderCounter = #WorkOderCounter
)
begin
/*
If the given work order counter already exists
then the next available one should be assigned.
*/
while exists
(
select 1
from dbo.WorkOrders as wo
where wo.WorkOrderYear = #WorkOrderYear
and wo.WorkOrderCounter = #WorkOderCounter
)
begin
set #WorkOderCounter = #WorkOderCounter + 1;
end;
end;
end;
/*
The actual insert of the new work order ID
*/
insert into dbo.WorkOrders
(
WorkOrderYear,
WorkOrderCounter,
WorkOrderDescription
)
values
(#WorkOrderYear,
#WorkOderCounter,
#WorkOrderDescription
);
end;
go
/*
Some test runs with the new table and stored procedure...
*/
exec dbo.newWorkOrder #WorkOrderYear = null,
#WorkOderCounter = null,
#WorkOrderDescription = null;
exec dbo.newWorkOrder #WorkOrderYear = null,
#WorkOderCounter = 3,
#WorkOrderDescription = null;
exec dbo.newWorkOrder #WorkOrderYear = null,
#WorkOderCounter = 0,
#WorkOrderDescription = null;
exec dbo.newWorkOrder #WorkOrderYear = null,
#WorkOderCounter = 0,
#WorkOrderDescription = null;
exec dbo.newWorkOrder #WorkOrderYear = null,
#WorkOderCounter = 0,
#WorkOrderDescription = null;
/*
...reviewing the result of the above.
*/
select *
from dbo.WorkOrders as wo;
Note, that the "next available" work order counter is once given (1) as the maximum + 1 and once (2) increased until it does not violate the unique key constraint on the table anymore. Like this you have two different possibilities to go about it.
There are a number of observations based on your code that you could alter to optimize and guarantee your results.
I am not aware of your Table Structure, but it seems you are using natural keys for your IDs.
Instead, use a surrogate key, such as INT/BIGINT to not only add efficiency in your table joins (no strings required), but potentially add another layer of security in your current design.
Alternatively, normalize the column into the flags they are. For example: OT-001-05 has three elements: OT is a type of work order, 001 is the ID, and 15 is the year. Presently, OT determines the ID which determines the year.
SELECT #aux = id_workorder FROM idaux;
idaux was not described. Is it a single value? If tabular, guarantee the result or it might break in the future.
Even if you add MAX(id_workorder), your result will not work as you think. Since this is a VARCHAR, the greatest value of the leftmost character not tied will return.
#aux, CHARINDEX('-', #aux) + 1,
LEN(#aux) - CHARINDEX('-', #aux) - CHARINDEX('-', REVERSE(#aux)));
This is fine, but overall you could make the code clearer and easier to debug by splitting all three of those elements into their own variable. Your still using your method, but simplified a little (personally, CHARINDEX can be a pain).
SET #aux = #Type -- 'OT'
SET #aux2 = #ID -- The result of your previous code
SET #aux3 = #Year -- your YY from GETDATE()
-- then join
SET #Work_Order = CONCAT(#aux, '-', #aux2, '-', #aux3)
Update:
Currently, your column in idaux has the ID in the MIDDLE of your column. This will produce disastrous results since any comparison of IDs will happen in the middle of the column. This means at best you might get away with PATINDEX but are still performing a table scan on the table. No index (save for FULLTEXT) will be utilized much less optimized.
I should add, if you put the ID element into its own column, you might find using BINARY collations on the column will improve its performance. Note I have not tested attempting a BINARY collation on a mixed column

Resources