Get String between two strings - SQL Server - sql-server-2008

I got a function that returns string between two strings:
CREATE FUNCTION dbo.udf_GetStringBetween2Chars (#String VARCHAR(50), #FirstSpecialChar VARCHAR(50), #LastSpecialChar VARCHAR(50))
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE #FirstIndexOfChar INT,
#LastIndexOfChar INT,
#LengthOfStringBetweenChars INT
SET #FirstIndexOfChar = CHARINDEX(#FirstSpecialChar,#String,0)
SET #LastIndexOfChar = CHARINDEX(#LastSpecialChar,#String,#FirstIndexOfChar+1)
SET #LengthOfStringBetweenChars = #LastIndexOfChar - #FirstIndexOfChar -1
SET #String = SUBSTRING(#String,#FirstIndexOfChar+1,#LengthOfStringBetweenChars)
RETURN #String
END
However, when I try to get string between POINTDESCRIPTION and DATAPOINT, I get error:
SET ANSI_WARNINGS OFF
declare #table table
(string varchar(50))
insert into #table
select 'EVENT: ID Order Reassignment (Individual Job Specific Update)|||LOCATION: Reassign.reassign()|||DATEPOINTDESCRIPTION: Only specific ID orders were reassigned from user fatis to user blake.|||DATAPOINT: blake' union all
select 'EVENT: ID Order Reassignment (Individual Job Specific Update)|||LOCATION: Reassign.reassign()|||DATAPOINTDESCRIPTION: Only specific ID orders were reassigned from user ilevic to user manic2.|||DATAPOINT: manic2' union all
select 'EVENT: ID Order Reassignment (Individual Job Specific Update)|||LOCATION: Reassign.reassign()|||DATAPOINTDESCRIPTION: Only specific ID orders were reassigned from user links to user sepir.|||DATAPOINT: sepir'
select dbo.udf_GetStringBetween2Chars (Tab.string,'POINTDESCRIPTION: ','|||DATAPOINT')
FROM #table Tab
Msg 537, Level 16, State 2, Line 10
Invalid length parameter passed to the LEFT or SUBSTRING function.
Does anyone see why this would happen?

If anyone finds it useful, here is final function to return string between 2 strings:
CREATE FUNCTION dbo.udf_GetStringBetween2Chars (#String VARCHAR(500), #FirstSpecialChar VARCHAR(500), #LastSpecialChar VARCHAR(500))
RETURNS VARCHAR(500)
AS
BEGIN
DECLARE #FirstIndexOfChar INT,
#LastIndexOfChar INT,
#LengthOfStringBetweenChars INT
SET #FirstIndexOfChar = CHARINDEX(#FirstSpecialChar,#String,0)
SET #LastIndexOfChar = CHARINDEX(#LastSpecialChar,#String,#FirstIndexOfChar+1)
SET #LengthOfStringBetweenChars = #LastIndexOfChar - #FirstIndexOfChar -1
SET #String = SUBSTRING(#String,#FirstIndexOfChar+LEN(#FirstSpecialChar),#LengthOfStringBetweenChars)
RETURN #String
END
GO
And to call it:
select dbo.udf_GetStringBetween2Chars (tab.someString,'POINTDESCRIPTION: ','|||DATAPOINT')
FROM yourTable tab

Related

Mysql Stored Proc not returning a VARCHAR out parameter

Below is my stored procedure. It works fine but my problem is I can't get the output parameter as VARCHAR.
The part where I'm having problem is the assignment of #curcName to the out parameter op_resultMessage
BEGIN
SET op_resultMessage = #curcName;
END;
Here's the Stored Procedure.
CREATE DEFINER=`root`#`localhost` PROCEDURE `addCurriculum`(
IN p_curcName varchar(100),
IN p_description TEXT,
IN p_yearLevel VARCHAR(50),
IN p_syStart INT,
IN p_syEnd INT,
IN p_creator VARCHAR(50),
OUT op_resultMessage VARCHAR(50))
BEGIN
DECLARE curcName VARCHAR(20) ;
IF EXISTS
(SELECT #curcName := `name`
FROM curriculum
WHERE
yearLevel = p_yearLevel
AND syStart = p_syStart
AND syEnd = p_syEnd )
THEN --
BEGIN
SET op_resultMessage = #curcName;
END;
ELSE
BEGIN
INSERT INTO curriculum(`name`, description, yearLevel, syStart, syEnd, creator)
VALUES(p_curcName,p_description,p_yearLevel,p_syStart,p_syEnd,p_creator);
END;
END IF;
END
I'm trying to return a message IF name EXISTS
So it should go something like
SET op_resultMessage = #curcName 'already uses the school year and year level you're trying to insert';
But I don't know how to properly concatenate and assign values. I'm still confused with := SET and = operators. I guess that's where I'm having problems with.
If I change the out parameter's type to an INT like
OUT op_resultMessage VARCHAR(50)
then assigns a number to op_resultMessage like SET op_resultMessage = 1;
It returns the number 1 as out parameter values. It just won't work with varchar.
So when I try to call the procedure
CALL `enrollmentdb`.`addCurriculum`
('Test Curriculum ','Test ','Grade 1',2015,2016,'jordan',#outputMsg);
SELECT #outputMsg; -- this doesn't return any value even if Grade 1, 2015 and 2016 exists
I'd appreciate any help. I actually just learned mysql recently.
Thanks.
drop procedure if exists addCurriculum;
delimiter $$
CREATE PROCEDURE `addCurriculum`(
IN p_curcName varchar(100),
IN p_description TEXT,
IN p_yearLevel VARCHAR(50),
IN p_syStart INT,
IN p_syEnd INT,
IN p_creator VARCHAR(50),
OUT op_resultMessage VARCHAR(50))
BEGIN
DECLARE curcName VARCHAR(20) ;
SELECT `name` into #curcName
FROM curriculum
WHERE
yearLevel = p_yearLevel
AND syStart = p_syStart
AND syEnd = p_syEnd
LIMIT 1;
-- Note change above. When selecting into a variable (or more than 1)
-- then 0 or 1 rows can come back max or an Error occurs
IF #curcName is not null then
SET op_resultMessage = #curcName;
ELSE
BEGIN
INSERT INTO curriculum(`name`, description, yearLevel, syStart, syEnd, creator)
VALUES(p_curcName,p_description,p_yearLevel,p_syStart,p_syEnd,p_creator);
END;
SET op_resultMessage = 'GEEZ I am right here'; -- Drew added this
END IF;
END$$
delimiter ;
Note the commentary in the stored procedure, especially the part of only 0 or 1 rows returning else an Error will occur with a select into var pattern. So LIMIT 1. That may or may not be the row you want (limit 1), but that is where it is at right now.

Loop items in list of user defined table types

I have simple table with 2 columns CarId int, primary key and CarName, varchar.
I need to create a stored procedure which accepts a list of cars. If car with CarId doesn't exist, I want to insert that car, and if already exists, I want to update it.
I created a user-defined table type CarType:
CREATE TYPE dbo.CarType
AS TABLE
(
CARID int null,
CARNAME varchar(800) not null,
);
and stored procedure InsertCars:
CREATE PROCEDURE dbo.InsertCars
#Cars AS CarType READONLY
AS
DECLARE #CarCount INT = 0;
DECLARE #Counter INT = 0;
BEGIN
SET #CarsCount = (SELECT COUNT(CarId) FROM #Cars);
WHILE(#Counter < #CarsCount)
BEGIN TRY
--how get item from list Cars?
#CurrentCar = Cars(#Counter)
IF EXISTS(SELECT 1 FROM Cars WHERE CarsId = CurrentCar.CarId)
--if doesn’t exist insert
BEGIN
INSERT INTO CARS(CARID, CARNAME)
SELECT * FROM #CurrentCar;
END
ELSE
BEGIN
--if exist update
END
END
SET #Counter= #Counter + 1;
END TRY
BEGIN CATCH
Print (ERROR_MESSAGE());
END CATCH
END
I don't know how get current car in loop from list of cars (parameter Cars in the stored procedure).
Or some elegant solution for this problem.
It seems you may get rid of loop here:
CREATE PROCEDURE dbo.InsertCars
#Cars AS CarType READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE c
SET c.CARNAME = c2.CARNAME
FROM Cars c
JOIN #Cars c2 on c2.CARID = c.CARID;
INSERT INTO Cars(CARID, CARNAME)
SELECT c.CARID, c.CARNAME
FROM #Cars c
WHERE NOT EXISTS (SELECT 1 FROM Cars WHERE CARID = c.CARID);
END
or (using merge construct):
CREATE PROCEDURE dbo.InsertCars
#Cars AS CarType READONLY
AS
BEGIN
SET NOCOUNT ON;
MERGE Cars AS target
USING (SELECT CARID, CARNAME FROM #Cars) AS source (CARID, CARNAME)
ON (target.CARID = source.CARID)
WHEN MATCHED THEN
UPDATE SET CARNAME = source.CARNAME
WHEN NOT MATCHED THEN
INSERT (CARID, CARNAME)
VALUES (source.CARID, source.CARNAME);
END

MySQL - Error 1064 in Stored Proc SQL

I have written the following stored procedure which in HeidiSQL is giving me an Error 1064 at the line starting with SET pay_ref = SELECT CONCAT('KOS' ...
Let me firstly explain what's going on with this procedure. I have a table gamers with a BIGINT primary key with auto_increment. This proc is supposed to:
Take in some params from the user
Check if the user already exists in the db according to his/her email address, and spits back the word "DUPLICATE" if a reord does exist
Else it does the insert as normal
Then it reads in the ID of the new record created and converts it to a varchar, pads it with leading zeros and then gets concatenated with some other strings
This new string (which should read for example KOS00001ABCDEF) then gets updated to the pay_refcode field >>> this is how we have settled on generating a unique payment reference for the user
If all works out well it updates retval with the newly generated reference code to be read by PHP script.
DELIMITER //
CREATE PROCEDURE `InsertGamer` (
IN p_fname VARCHAR(30),
IN p_lname VARCHAR(30),
IN p_email VARCHAR(255),
IN p_favgame VARCHAR(60),
IN p_pay_suffix VARCHAR(6),
OUT retval VARCHAR(14)
)
BEGIN
DECLARE last_id BIGINT;
DECLARE pay_ref VARCHAR(14);
IF (EXISTS(SELECT * FROM gamers WHERE (email = p_email))) THEN
SET retval = 'DUPLICATE';
ELSE
INSERT INTO gamers (fname, lname, email, favgame, pay_refcode)
VALUES (p_fname, p_lname, p_email, p_favgame, NULL);
SET last_id = LAST_INSERT_ID();
SET pay_ref = SELECT CONCAT('KOS', (SELECT LPAD(CONVERT(last_id, VARCHAR(5)),5,'0')), p_pay_suffix);
UPDATE gamers
SET pay_refcode = pay_ref
WHERE application_id = last_id;
SET retval = pay_ref;
END IF;
END //
I cannot for the life of me figure out what the problem is and would sincerely appreciate any help from you. Thank you very much in advance!
You just need to remove the SELECT keyword from line which you set the value for pay_ref.
SET pay_ref = CONCAT('KOS', LPAD(CONVERT(last_id, CHAR(5)),5,'0'), p_pay_suffix);
full code:
DELIMITER //
CREATE PROCEDURE `InsertGamer` (
IN p_fname VARCHAR(30),
IN p_lname VARCHAR(30),
IN p_email VARCHAR(255),
IN p_favgame VARCHAR(60),
IN p_pay_suffix VARCHAR(6),
OUT retval VARCHAR(14)
)
BEGIN
DECLARE last_id BIGINT;
DECLARE pay_ref VARCHAR(14);
SET #count := (SELECT COUNT(*) FROM gamers WHERE email = p_email)
IF (#count > 0) THEN
SET retval = 'DUPLICATE';
ELSE
INSERT INTO gamers (fname, lname, email, favgame, pay_refcode)
VALUES (p_fname, p_lname, p_email, p_favgame, NULL);
SET last_id = LAST_INSERT_ID();
SET pay_ref = CONCAT('KOS', LPAD(CONVERT(last_id, CHAR(5)),5,'0'), p_pay_suffix);
UPDATE gamers
SET pay_refcode = pay_ref
WHERE application_id = last_id;
SET retval = pay_ref;
END IF;
END //
DELIMITER ;

stored procedure for inserting comma seperated values in table using ms sql?

I have a table named assignRole.
I am passing string of userid (int) csv ,and passing roleid(int).
I want a stored procedure which split userid from string and take roleid and insert these values in table.this thing is to happen for all values in userid string.
First, create a function:
CREATE FUNCTION [dbo].[SplitInts]
(
#List VARCHAR(MAX),
#Delimiter CHAR(1)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN ( SELECT Item = CONVERT(INT, Item) FROM (
SELECT Item = x.i.value('(./text())[1]', 'int') FROM (
SELECT [XML] = CONVERT(XML, '<i>' + REPLACE(#List, #Delimiter, '</i><i>')
+ '</i>').query('.') ) AS a CROSS APPLY [XML].nodes('i') AS x(i)) AS y
WHERE Item IS NOT NULL
);
Now you can say:
INSERT dbo.assignRole(RoleID, UserID)
SELECT #RoleID, Item
FROM dbo.SplitInts(#UserIDList, ',');
I like to use a table-valued function to do the split.
IF OBJECT_ID (N'dbo.StrSplit') IS NOT NULL DROP FUNCTION dbo.[StrSplit]
GO
CREATE FUNCTION [dbo].[StrSplit]
(
#String VARCHAR(MAX), #Delimiter char(1)
)
RETURNS
#Results TABLE (
Items NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #INDEX INT
DECLARE #SLICE nvarchar(MAX)
-- HAVE TO SET TO 1 SO IT DOESNT EQUAL ZERO FIRST TIME IN LOOP
SELECT #INDEX = 1
-- Early exit if passed string is null
IF #String IS NULL RETURN
WHILE #INDEX !=0
BEGIN
-- GET THE INDEX OF THE FIRST OCCURENCE OF THE SPLIT CHARACTER
SELECT #INDEX = CHARINDEX(#Delimiter,#STRING)
-- NOW PUSH EVERYTHING TO THE LEFT OF IT INTO THE SLICE VARIABLE
IF #INDEX !=0
SELECT #SLICE = LEFT(#STRING,#INDEX - 1)
ELSE
SELECT #SLICE = #STRING
-- PUT THE ITEM INTO THE RESULTS SET
INSERT INTO #Results(Items) VALUES(#SLICE)
-- CHOP THE ITEM REMOVED OFF THE MAIN STRING
SELECT #STRING = RIGHT(#STRING,LEN(#STRING) - #INDEX)
-- BREAK OUT IF WE ARE DONE
IF LEN(#STRING) = 0 BREAK
END
RETURN
END
GO
GRANT SELECT ON [dbo].[StrSplit] TO [public]

serialising rows in a table

I have a table which contains header information for transactions. The transactions belong to different projects.
In the header I have columns:
rhguid - uniqueidentifier
rhserial - int
rh_projectID - int
First I insert the row (there's more columns)
Then I calculate the serial number for that project:
update responseheader
set rhSerial = 1 + (select isnull(max(rhSerial), 0)
from responseheader
where (rhstatus = 0) AND (rh_projectID = 1234))
where
(rhGUID = <preassignedGUID>);
However when there are many transactions happening at the same time for a project I am finding duplicate rhserial values.
I'm doing this in classic ASP with SQL Server 2008.
Is there a better way?
From your example, it doesn't look like you're using a transaction. My guess is that the SELECT portion of the statement is running as READ UNCOMMITTED, otherwise you would not see duplicates. There are ways to start transactions with ADO, but I prefer using stored procedures instead.
Try implementing something like this:
CREATE PROC dbo.ResponseHeader_Insert
<more data to insert>,
#ProjectID INT,
#Status SMALLINT
as
insert responseheader (column names here)
select <param values here>, isnull(max(rhSerial), 0) + 1
from responseheader
where (rhstatus = #Status) AND (rh_projectID = #ProjectID))
If this doesn't work for ya, try creating sequence tables (one for each sequence).
create table <tablename> (
SeqID int identity(1,1) primary key,
SeqVal varchar(1)
)
Create a procedure to get the next identity:
create procedure GetNewSeqVal_<tablename>
as
begin
declare #NewSeqValue int
set NOCOUNT ON
insert into <tablename> (SeqVal) values ('a')
set #NewSeqValue = scope_identity()
delete from <tablename> WITH (READPAST)
return #NewSeqValue
end
If there are too many sequence tables that need to be created or you want to create sequences on the fly, try this approach:
Create table AllSequences (
SeqName nvarchar(255) primary key, -- name of the sequence
Seed int not null default(1), -- seed value
Incr int not null default(1), -- incremental
Currval int
)
Go
create procedure usp_CreateNewSeq
#SeqName nvarchar(255),
#seed int = 0,
#incr int = 1
as
begin
declare #currval int
if exists (
select 1 from AllSequences
where SeqName = #SeqName )
begin
print 'Sequence already exists.'
return 1
end
if #seed is null set #seed = 1
if #incr is null set #incr = 1
set #currval = #seed
insert into AllSequences (SeqName, Seed, Incr, CurrVal)
values (#SeqName, #Seed, #Incr, #CurrVal)
end
go
create procedure usp_GetNewSeqVal
#SeqName nvarchar(255)
as
begin
declare #NewSeqVal int
set NOCOUNT ON
update AllSequences
set #NewSeqVal = CurrVal = CurrVal+Incr
where SeqName = #SeqName
if ##rowcount = 0 begin
print 'Sequence does not exist'
return
end
return #NewSeqVal
end
go