SQL Server function with count doesn't work as intended - sql

I am working on a project for class & course management. I want to create a function that automatically creates a course code based on the course name and then adds a number based on whether there already exist a course name with the same first 4 characters.
Here is what my function looks like:
CREATE FUNCTION [dbo].[fxGenerateCourseCode]
(#course_name VARCHAR(50))
RETURNS VARCHAR(8)
AS
BEGIN
DECLARE #course_code VARCHAR(8), #count_course INT
SET #count_course = (SELECT COUNT(1) FROM dbo.COURSE_TB
WHERE SUBSTRING(course_name, 1, 4) = SUBSTRING(#course_name, 1, 4)) + 1
SELECT #course_code = UPPER(SUBSTRING(#course_name, 1, 4)) + ' - ' + '00' + CAST(#count_course AS VARCHAR)
RETURN #course_code
END
The problem is, whenever I execute this function, it is not returning the right count values and it doesn't include the 00 either.
For example, executing this:
SELECT dbo.fxGenerateCourseCode('french')
returns FREN-0 rather than FREN-001 (which is what I expect).
However, when I execute the code manually, it gives me the result that I expect:
DECLARE #course_code VARCHAR(8), #count_course INT, #course_name VARCHAR(50)
SET #course_name = 'french'
SET #count_course = (SELECT COUNT(1) + 1 FROM dbo.PARAMETRES_COURS
WHERE SUBSTRING(nom_cours, 1, 4) = SUBSTRING(#course_name, 1, 4))
SELECT course_code = UPPER(SUBSTRING(#course_name, 1, 4)) + ' - ' + '00' + CAST(#count_course AS VARCHAR)
Result: FREN-001.
I have tried changing the way I write the function with not much result. Can someone help me know where the problem here is coming from? Am I doing something wrong in the function?
Thank you

I suspect that you are putting the value into a column with a length of 6. That said, the problem would appear to be the spaces around the hyphen. Also, always include a length for varchar, because the defaut length varies by context and it might not do what you want:
You can simplify your code and make it more accurate:
declare #course_code varchar(8), #count_course int;
select #count_course = count(1) + 1
from dbo.COURSE_TB
where left(course_name, 4) = left(#course_name, 4);
select #course_code = concat(left(upper(#course_name, 4)), '-', '00', #count_course);
Note that this still doesn't do what you want, which is padding the number. This version only works for 9 courses. You seem to want:
select #course_code = concat(left(upper(#course_name, 4)), '-',
right(concat('00', #count_course), 3)
);
Note the use of the concat() function, so you don't have to worry about lengths of strings when doing a conversion.

Related

SQL Server 2012 string functions

I have a field that can vary in length of the format CxxRyyy where x and y are numeric. I want to choose xx and yyy. For instance, if the field value is C1R12, then I want to get 1 and 12. if I use substring and charindex then I have to use a length, but I would like to use a position like
SUBSTRING(WPLocationNew, CHARINDEX('C',WPLocationNew,1)+1, CHARINDEX('R',WPLocationNew,1)-1)
or
SUBSTRING(WPLocationNew, CHARINDEX('C',WPLocationNew,1)+1, LEN(WPLocationNew) - CHARINDEX('R',WPLocationNew,1))
to get x, but I know that doesn't work. I feel like there is a fairly simple solution, but I am not coming up with it yet. Any suggestions
If these are cell references and will always be in the form C{1-5 digits}R{1-5 digits} you can do this:
DECLARE #t TABLE(Original varchar(32));
INSERT #t(Original) VALUES ('C14R4535'),('C1R12'),('C57R123');
;WITH src AS
(
SELECT Original, c = REPLACE(REPLACE(Original,'C',''),'R','.')
FROM #t
)
SELECT Original, C = PARSENAME(c,2), R = PARSENAME(c,1)
FROM src;
Output
Original
C
R
C14R4535
14
4535
C1R12
1
12
C57R123
57
123
Example db<>fiddle
If you need to protect against other formats, you can add
FROM #t WHERE Original LIKE 'C%[0-9]%R%[0-9]%'
AND PATINDEX('%[^C^R^0-9]%', Original) = 0
Updated db<>fiddle
It appears that you are attempting to parse an Excel cell reference. Those are predictably structured or I wouldn't suggest such an embarrassing hack as this.
Basically, take advantage of the fact that a try_cast in SQL ignores spaces when converting strings to numbers.
declare #val as varchar(20) = 'C1R12'
declare #newval as varchar(20)
declare #c as smallint
declare #r as smallint
--replace the C with 5 spaces
set #newval = replace(#val,'C',' ')
--replace the R with 5 spaces
set #newval = replace(#newval,'R',' ')
--take a look at the intermediate result, which is ' 1 14'
select #newval
set #c = try_cast(left(#newval,11) as smallint)
set #r = try_cast(right(#newval,6) as smallint)
--take a look at the results... two smallint, 1 and 14
select #c, #r
That can all be accomplished in one line for each element (a line for column and a line for row) but I wanted you to be able to understand what was happening so this example goes through the steps individually.
Here's yet another way:
declare #val as varchar(20) = 'C12R345'
declare #c as varchar(5)
declare #r as varchar(5)
set #c = SUBSTRING(#val, patindex('C%', #val)+1,(patindex('%R%', #val)-1)-patindex('C%', #val) )
set #r = SUBSTRING(#val, patindex('%R%', #val)+1, LEN(#val) -patindex('%R%', #val))
select cast(#c as int) as 'C', cast(#r as int) as 'R'
dbfiddle
There are lots of different ways to approach string parsing. Here's just one possible idea:
declare #s varchar(10) = 'C01R002';
select
rtrim( left(replace(stuff(#s, 1, 1, ''), 'R', ' '), 10)) as c,
ltrim(right(replace(substring(#s, 2, 10), 'R', ' '), 10)) as r
Strip out the 'C' and then replace the 'R' with enough spaces so that the left and right sides can be extracted using a fixed length and then easily trimmed back.
stuff() and substring() as used above are just different ways accomplish exactly the same thing. One advantage here is that it does use fairly portable string functions and it's conceivable that this is somewhat faster. This is also done inline and without multiple steps.

Extract substring from string if certain characters exists SQL

I have a string:
DECLARE #UserComment AS VARCHAR(1000) = 'bjones marked inspection on system UP for site COL01545 as Refused to COD won''t pay upfront :Routeid: 12 :Inspectionid: 55274'
Is there a way for me to extract everything from the string after 'Inspectionid: ' leaving me just the InspectionID to save into a variable?
Your example doesn't quite work correctly. You defined your variable as varchar(100) but there are more characters in your string than that.
This should work based on your sample data.
DECLARE #UserComment AS VARCHAR(1000) = 'bjones marked inspection on system UP for site COL01545 as Refused to COD won''t pay upfront :Routeid: 12 :Inspectionid: 55274'
select right(#UserComment, case when charindex('Inspectionid: ', #UserComment, 0) > 0 then len(#UserComment) - charindex('Inspectionid: ', #UserComment, 0) - 13 else len(#UserComment) end)
I would do this as:
select stuff(#UserComment, 1, charindex(':Inspectionid: ', #UserComment) + 14, '')
This works even if the string is not found -- although it will return the whole string. To get an empty string in this case:
select stuff(#UserComment, 1, charindex(':Inspectionid: ', #UserComment + ':Inspectionid: ') + 14, '')
Firstly, let me say that your #UserComment variable is not long enough to contain the text you're putting into it. Increase the size of that first.
The SQL below will extract the value:
DECLARE #UserComment AS VARCHAR(1000); SET #UserComment = 'bjones marked inspection on system UP for site COL01545 as Refused to COD won''t pay upfront :Routeid: 12 :Inspectionid: 55274'
DECLARE #pos int
DECLARE #InspectionId int
DECLARE #IdToFind varchar(100)
SET #IdToFind = 'Inspectionid: '
SET #pos = CHARINDEX(#IdToFind, #UserComment)
IF #pos > 0
BEGIN
SET #InspectionId = CAST(SUBSTRING(#UserComment, #pos+LEN(#IdToFind)+1, (LEN(#UserComment) - #pos) + 1) AS INT)
PRINT #InspectionId
END
You could make the above code into a SQL function if necessary.
If the Inspection ID is always 5 digits then the last argument for the Substring function (length) can be 5, i.e.
SELECT SUBSTRING(#UserComment,PATINDEX('%Inspectionid:%',#UserComment)+14,5)
If the Inspection ID varies (but is always at the end - which your question slightly implies), then the last argument can be derived by subtracting the position of 'InspectionID:' from the overall length of the string. Like this:
SELECT SUBSTRING(#UserComment,PATINDEX('%Inspectionid:%',#UserComment)+14,LEN(#usercomment)-(PATINDEX('%Inspectionid:%',#UserComment)+13))

T-SQL How to create function that compares string, checks difference, and do special function

First - sorry for my english. Second - i'm learning t-SQL.
Goal:
I want to get difference between two strings, then check in which column is this difference. If the difference is in first column, do something, if in second column - do something else.
What I'm actually doing
Column 'messages' is a string which contains list of ID. So i am replacing all '#' with ',' and deleting last ',' what gives to me ActualID and BeforeID column. See below:
DECLARE #string VARCHAR(512);
DECLARE #string2 VARCHAR(512);
DECLARE #string3 VARCHAR(512);
SET #string = '41#42#43#44#45#46#47#48#49#50#51#52#53#54#55#56#57#58#59#';
SET #string2 = REPLACE((SELECT messages FROM USERS WHERE userid = 4), '#', ', ' )
SET #string3 = left(#string2, len(#string2) - 1);
SET #string2 = REPLACE(#string, '#', ', ' )
SET #string = left(#string2, len(#string2) - 1);
SELECT #string3 as ActualID, #string as BeforeID
So now, I want compare BeforeID with ActualID. For example:
In BeforeID we have 1, 2, 3 / In ActualID 1, 2, 3, 4
In example above 4 was added. So, if it was added I want to add it to #AddedElements.
If 4, 5, 7 were added then SELECT #AddedElements as AddedElements should return 4, 5, 7 (With comas)
But, that's not all.
If BeforeID = 1, 5, 10, 14 and ActualID = 1, 5, 14 I want, that element which is in BeforeID, but not in AcutalID will be added to #DeletedElements.
So SELECT #DeletedElements as DeletedElements should return 10
Added elements/Deleted elements should be returned once. I mean, full result what I want to Earn should be
SELECT #AddedElements as AddedElements, #DeletedElements as DeletedElements
Is it possible? If, then how to do it?
First of all, I have to start by saying that this is just poor design; but having said that, I've also found myself in all kinds of situations where I couldn't change the way things worked, only try to make them work better in the current configuration. Therefore, I recommend something like this:
1: Create a UDF (User-Defined Function) that can handle splitting the strings and returning them in table-formed data that you can work with:
CREATE FUNCTION [dbo].[UDF_StringDelimiter]
/*********************************************************
** Takes Parameter "LIST" and transforms it for use **
** to select individual values or ranges of values. **
** **
** EX: 'This,is,a,test' = 'This' 'Is' 'A' 'Test' **
*********************************************************/
(
#LIST VARCHAR(8000)
,#DELIMITER VARCHAR(255)
)
RETURNS #TABLE TABLE
(
[RowID] INT IDENTITY
,[Value] VARCHAR(255)
)
WITH SCHEMABINDING
AS
BEGIN
DECLARE
#LISTLENGTH AS SMALLINT
,#LISTCURSOR AS SMALLINT
,#VALUE AS VARCHAR(255)
;
SELECT
#LISTLENGTH = LEN(#LIST) - LEN(REPLACE(#LIST,#DELIMITER,'')) + 1
,#LISTCURSOR = 1
,#VALUE = ''
;
WHILE #LISTCURSOR <= #LISTLENGTH
BEGIN
INSERT INTO #TABLE (Value)
SELECT
CASE
WHEN #LISTCURSOR < #LISTLENGTH
THEN SUBSTRING(#LIST,1,PATINDEX('%' + #DELIMITER + '%',#LIST) - 1)
ELSE SUBSTRING(#LIST,1,LEN(#LIST))
END
;
SET #LIST = STUFF(#LIST,1,PATINDEX('%' + #DELIMITER + '%',#LIST),'')
;
SET #LISTCURSOR = #LISTCURSOR + 1
;
END
;
RETURN
;
END
;
2: Consider dropping the whole "Switching out commas" thing, because it's pointless - the function I've written here takes two arguments: The string itself, and the delimiter (the mini-string that separates the individual strings within the big string, in your case '#') Then you just have to do a couple of quick comparisons to find out what was added and what was deleted.
DECLARE
#AddedElements VARCHAR(255) = ''
,#DeletedElements VARCHAR(255) = ''
,#ActualID VARCHAR(255) = '41#42#43#44#45#46#47#48#49#50#51#52#53#54#55#56#57#58#59#'
,#BeforeID VARCHAR(255) = '41#42#43#44#45#46#47#48#50#51#52#53#54#55#56#57#58#59#60#'
;
SET #AddedElements = #AddedElements +
SUBSTRING(
(
SELECT ', ' + Value
FROM dbo.UDF_StringDelimiter(#ActualID,'#')
WHERE Value NOT IN
(
SELECT Value
FROM dbo.UDF_StringDelimiter(#BeforeID,'#')
)
GROUP BY ', ' + Value
FOR XML PATH('')
)
,3,255)
;
SET #DeletedElements = #DeletedElements +
SUBSTRING(
(
SELECT ', ' + Value
FROM dbo.UDF_StringDelimiter(#BeforeID,'#')
WHERE Value NOT IN
(
SELECT Value
FROM dbo.UDF_StringDelimiter(#ActualID,'#')
)
GROUP BY ', ' + Value
FOR XML PATH('')
)
,3,255)
;
SELECT #AddedElements AS AddedElements,#DeletedElements AS DeletedElements
;
Using this method, if you add a value to #ActualID that does not exist in #BeforeID, it will show up in #AddedElements.
Likewise , if you remove an element from #ActualID that had previously existed in #BeforeID, it will show up in #DeletedElements.
All of this is, of course, assuming that the dynamic string (the one really being compared here) is the #ActualID. I operated with the understanding that #BeforeID is actually a stored value in the DB, and #ActualID is a dynamic string being passed in from...somewhere. If this is wrong, update me and I'll change the tactic appropriately.
Quick note: It's important to me to point out that this is just one way of dealing with a situation like this, and I'm sure there are better ways; but with the information I have, it's the best I could come up with without spending too much time and energy on it.

How can I find part of a string between two words?

I want to get a part of text from my field description
Could someone offer some advice?
The whole string is 'Version100][BuildNumber:666][SubBuild:000]' and the build number is what I want to single out (however the number may change).
I have tried SUBSTRING with CHARINDEX but I can't seem to figure it out.
I've been googling for about 30 minutes and I can't seem to work it out.
little long, but you could do this.
DECLARE #Description VARCHAR(MAX)= '[Version100][BuildNumber:666][SubBuild:000]'
SELECT LEFT(STUFF(#Description, 1, PATINDEX('%BuildNumber%', #Description) + 11, '' )
,PATINDEX('%]%', STUFF(#Description, 1, PATINDEX('%BuildNumber%', #Description) + 11, '' )) - 1)
You can try this:
SELECT SUBSTRING([description],CHARINDEX('BuildNumber:',[description])+12,
CHARINDEX(']',[description], CHARINDEX('BuildNumber:',[description]))
-(CHARINDEX('BuildNumber:',[description])+12))
FROM YOURTABLE
I haven't actually tested this (numeric offsets might be a bit off, but you can tweak them), but assuming the string formats are always the same, then the following should work, irrespective of the number of digits in the build number.
SELECT SUBSTRING(description,
CHARINDEX('BuildNumber', description) + 11, --The position after BuildNumber: (a)
CHARINDEX('][SubBuild', description) - 3 - CHARINDEX('BuildNumber', description) + 11)) --The distance from (a) to the square brackets before SubBuild
Hope this query can return your expected result with out hard-coding the build number count:
DECLARE #Description AS VARCHAR (500) = 'Version100][BuildNumber:6663211][SubBuild:000]';
DECLARE #BeforeString AS VARCHAR (100) = '[BuildNumber:';
DECLARE #BeforeStringPosition AS INT = CHARINDEX(#BeforeString, #Description);
SELECT SUBSTRING(#Description, #BeforeStringPosition + LEN(#BeforeString) , CHARINDEX('][SubBuild', #Description) - #BeforeStringPosition - LEN(#BeforeString));

Replace Last character in SQL Server 2008

I am working with SQL server 2008, and facing problem about character replacement.
If I use
SELECT REPLACE(MYWORD,0,1) FROM MYTABLE
It is replacing all 0 into 1, I just want to replace Last character Like MYWORD = "ERMN0" so it will be MYWORD = "ERMN1"
using STUFF, which, IMO, ends up being most readable:
DECLARE #MyWORD VARCHAR(20) = 'ABCDEF123'
SELECT STUFF(#MyWORD, LEN(#MyWORD), 1, '2')
output:
ABCDEF122
You may use combination of LEFT, RIGHT, and CASE.
You need to use CASE to check the most RIGHT character whether it's a 0 or not and replace it with 1. And at last, combine it with the LEFT part (after being separated from the last character) of the MYWORD string.
However, depending on your requirement, it may have a drawback.
When there is a word ending with 10, it would also be replaced.
SELECT LEFT(MYWORD,LEN(MYWORD)-1) + CASE RIGHT(MYWORD,1) WHEN '0' THEN '1' ELSE RIGHT(MYWORD,1) END
Try this.
SELECT LEFT('ERMN0', Len('ERMN0')-1)
+ Replace(RIGHT('ERMN0', 1), 0, 1)
OUTPUT : ERMN1
In your case
SELECT LEFT(MYWORD, Len(MYWORD)-1)
+ Replace(RIGHT(MYWORD, 1), 0, 1) as [REPLACED] FROM MYTABLE
Try this
SELECT SUBSTRING(MYWORD, 1, LEN(MYWORD) - 1) +
REPLACE(SUBSTRING(MYWORD, LEN(MYWORD), LEN(MYWORD)), 0, 1) FROM MYTABLE
This will work
SELECT LEFT ('ERMN0' , Len('ERMN0') -1 ) + REPLACE(Right('ERMN0', 1), '0','1')
Or in your case
SELECT LEFT (MYWORD , Len(MYWORD) -1 ) + REPLACE(Right(MYWORD, 1), '0','1') AS MYWORD FROM MYTABLE
this is also use full to replace letters from end
It is used from replacing characters from end 1,2 or N
Declare #Name nvarchar(20) = 'Bollywood'
select #Name = REPLACE(#Name, SUBSTRING(#Name, len(#Name) - 1, 2), 'as')
SELECT #Name
output is "Bollywoas"
Here best part is you can repalce as many character from last you needed.