Issue while execute tsql function - function

I have written the following function:
CREATE FUNCTION dbo.GetData
(#value AS VARCHAR(MAX),
#pattern AS VARCHAR(MAX),
#masker AS VARCHAR,
#notMaskedCount AS INT)
RETURNS NCHAR(47)
AS BEGIN
DECLARE #nextPatIdx INT
SET #nextPatIdx = PATINDEX('%' + #pattern + '%', #value)
WHILE #nextPatIdx > 0 AND #nextPatIdx < LEN(#value) - #notMaskedCount
BEGIN
SET #value = Stuff(#value, #nextPatIdx, 1, #masker)
SET #nextPatIdx = PATINDEX('%' + #pattern + '%', #value)
END
RETURN CONVERT(NCHAR(40), #value) + '_data'
END
Run it with :
select dbo.GetData('152648494','[a-zA-Z0-9]', 'x', 4)
If I execute it throw C# and try readdata it throws exception
If I runt it viahttp://sqlfiddle.com/#!6/6ee81/1 I get an error
It doen`t complete the execution
What is wrong - the function / the way I call it?

You achieved a infinite loop as you can check using this piece of code:
---select dbo.GetData('152648494','[a-zA-Z0-9]', 'x', 4)
declare
#value AS VARCHAR(MAX) = '152648494',
#pattern AS VARCHAR(MAX) = '[a-zA-Z0-9]',
#masker AS VARCHAR(1) = 'x',
#notMaskedCount AS INT = 4
DECLARE #nextPatIdx INT
SET #nextPatIdx = PATINDEX('%' + #pattern + '%', #value)
WHILE #nextPatIdx > 0 AND #nextPatIdx < LEN(#value) - #notMaskedCount
BEGIN
SET #value = Stuff(#value, #nextPatIdx, 1, #masker)
SET #nextPatIdx = PATINDEX('%' + #pattern + '%', #value)
print #nextPatIdx;
END
print 'END'
select CONVERT(NCHAR(40), #value) + '_data'
Maybe if you change the inequality
#nextPatIdx > 0
to
#nextPatIdx > 1
It ill exit the infinite loop but I need more info to find out what you desired output
EDIT
I guess you need to change your loop to it.
WHILE #nextPatIdx > 0 AND #nextPatIdx <= LEN(#value) - #notMaskedCount
BEGIN
SET #value = Stuff(#value, #nextPatIdx, 1, #masker)
--SET #nextPatIdx = PATINDEX('%' + #pattern + '%', #value)
SET #nextPatIdx += 1
END

Related

Stored procedure cast error

I have a strange problem with the following SP:
CREATE PROCEDURE [dbo].[Manufacturer_GetProductsCountByManufacturedId]
(
#ManufacturerIds NVARCHAR(MAX) = '',
#ExcludeType INT
)
AS
BEGIN
DECLARE #ManufacturerId NVARCHAR(MAX)
DECLARE #GoodManufacturerIds NVARCHAR(MAX) = ''
DECLARE #result BIGINT
DECLARE #pos INT
DECLARE #len INT
SET #GoodManufacturerIds = ''
SET #pos = 0
SET #len = 0
WHILE CHARINDEX(',', #ManufacturerIds, #pos + 1)>0
BEGIN
-- Split
SET #len = CHARINDEX(',', #ManufacturerIds, #pos+1) - #pos
SET #ManufacturerId = SUBSTRING(#ManufacturerIds, #pos, #len)
-- Check
SELECT TOP 1 #result = p.ProductId
FROM [MYDB].[dbo].[Product] p
INNER JOIN [MYDB].[ProductTyping].[TypedProductFieldValue] tpfv ON tpfv.ProductId = p.ProductId
WHERE ManufacturerId = #ManufacturerId
AND tpfv.ProductTypeId <> #ExcludeType
IF #result > 0
SET #GoodManufacturerIds = #GoodManufacturerIds + #ManufacturerId + ','
SET #pos = CHARINDEX(',', #ManufacturerIds, #pos+#len)+1
END
-- Last comma kaputt
SET #GoodManufacturerIds = LEFT(#GoodManufacturerIds, LEN(#GoodManufacturerIds) - 1)
RETURN #GoodManufacturerIds
END
Basically I have to split a string with comma-separated values and for each on those values, I execute a query and put the result in another comma-separated string.
If I send these values as stored procedure parameters, ("5220,3008,1561,2678," and 5) I got an error, "Unable to cast nvarchar values '5220,3008,1561,2678' to type int".
BUT If I built in those values in the stored procedure as:
DECLARE #ManufacturerId NVARCHAR(MAX)
DECLARE #GoodManufacturerIds NVARCHAR(MAX) = ''
DECLARE #result BIGINT
DECLARE #pos INT
DECLARE #len INT
-- test
DECLARE #ManufacturerIds NVARCHAR(MAX) = ''
DECLARE #ExcludeType INT
SET #ManufacturerIds = '5220,3008,1561,2678,13715,5047,'
SET #ExcludeType = 5
SET #GoodManufacturerIds = ''
SET #pos = 0
SET #len = 0
WHILE CHARINDEX(',', #ManufacturerIds, #pos + 1)>0
BEGIN
-- Split
SET #len = CHARINDEX(',', #ManufacturerIds, #pos+1) - #pos
SET #ManufacturerId = SUBSTRING(#ManufacturerIds, #pos, #len)
-- Check
SELECT TOP 1 #result = p.ProductId
FROM [MYDB].[dbo].[Product] p
INNER JOIN [MYDB].[ProductTyping].[TypedProductFieldValue] tpfv ON tpfv.ProductId = p.ProductId
WHERE ManufacturerId = #ManufacturerId
AND tpfv.ProductTypeId <> #ExcludeType
IF #result > 0
SET #GoodManufacturerIds = #GoodManufacturerIds + #ManufacturerId + ','
SET #pos = CHARINDEX(',', #ManufacturerIds, #pos+#len)+1
END
-- Last comma kaputt
SET #GoodManufacturerIds = LEFT(#GoodManufacturerIds, LEN(#GoodManufacturerIds) - 1)
RETURN #GoodManufacturerIds
Everything works fine and I got my new string.
I dunno where the error could be in the first procedure, any hints?
I found the error, I was using RETURN but RETURN is for integer values and in this case mine is a string, so putting SELECT make everything works fine.

Mysql : Not allowed to return a result set from a function

I have write one function but getting this error Not allowed to return a result set from a function
DELIMITER $$
CREATE FUNCTION getTestFunction
(
p_ParentID int,
p_ListName nvarchar(50),
p_Type nvarchar(50),
p_Count int
)
RETURNS nvarchar(2000)
BEGIN
DECLARE p_KeyValue nvarchar(2000);
DECLARE p_ListValue nvarchar(2000);
DECLARE p_TextValue nvarchar(2000);
DECLARE p_ReturnValue nvarchar(2000);
DECLARE p_Key nvarchar(2000);
IF p_ParentID = 0 THEN
IF p_Count = 0 THEN
SET p_ReturnValue = '';
ELSE
SET p_ReturnValue = p_ListName;
END IF;
ELSE
SELECT p_KeyValue = ListName + '.' + Value
FROM ListsTable
WHERE EntryID = p_ParentID LIMIT 1 ;
RETURN p_ReturnValue;
If p_Type = 'ParentKey' Or (p_Type = 'ParentList' AND p_Count > 0) THEN
SET p_ReturnValue = p_KeyValue;
ELSE
IF p_Type = 'ParentList' THEN
SET p_ReturnValue = p_ListValue;
ELSE
SET p_ReturnValue = p_TextValue;
END IF;
END IF;
IF p_Count > 0 THEN
If p_Count = 1 AND p_Type = 'ParentList' THEN
SET p_ReturnValue = p_ReturnValue + ':' + p_ListName;
ELSE
SET p_ReturnValue = p_ReturnValue + '.' + p_ListName;
END IF;
END IF;
END IF;
RETURN p_ReturnValue;
END$$
DELIMITER ;
You want to assign the result of a query to a variable, but in fact you're just selecting. That's why MySQL's complaining.
You have to change this
SELECT p_KeyValue = ListName + '.' + Value
FROM ListsTable
WHERE EntryID = p_ParentID LIMIT 1 ;
to
SELECT CONCAT(ListName, '.', `Value`)
INTO p_KeyValue
FROM ListsTable
WHERE EntryID = p_ParentID LIMIT 1 ;
And you should add an ORDER BY. A LIMIT without ORDER BY doesn't make sense, since there's no guaranteed order in a relational database.
Mysql complains about SELECT statement in your function,
probably it understands SELECT p_KeyValue = ListName + '.' + Value as comparison
change it to
SELECT CONCAT(ListName, '.', Value) INTO p_KeyValue

Converting MSSQL function to MySQL function

I am trying to convert this user defined function (taken from a MSSQL) and tweak it so that I can run it MYSQL. I have made several attempts but all seem to error on the declare variable.
I am running the following version: 5.6.11-log - MySQL Community Server (GPL)
USE [DataGB]
GO
/****** Object: UserDefinedFunction [dbo].[FullPostCodeFix] Script Date: 11/20/2013 16:10:44 ******/
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
CREATE FUNCTION [dbo].[FullPostCodeFix] (#Postcode VARCHAR(20))
RETURNS VARCHAR(20)
BEGIN
/*
Puts postcode into correct format if it is currently in any of the below formats
AB12AA
AB 12AA
AB1 2AA
AB 1 2AA
Returns #Postcode
*/
DECL ARE #District Varchar(50)
DECLARE #Remainder Varchar(50)
DECLARE #Sector Varchar(50)
SET #District= CASE
WHEN LEN(#Postcode) - CHARINDEX(' ', REVERSE(#Postcode)) = len(#Postcode) THEN SUBSTRING(#Postcode,1,(len(#Postcode) - 3))
WHEN LEN(#Postcode) - CHARINDEX(' ', REVERSE(#Postcode)) < 3 THEN SUBSTRING(#Postcode,1,(len(#Postcode) - 3))
ELSE SUBSTRING(#Postcode, 0, LEN(#Postcode) - CHARINDEX(' ', REVERSE(#Postcode)) + 1)
END
SET #District = dbo.PostcodeFix(#District)
SET #Remainder= RIGHT(#Postcode,3)
SET #Sector = #District + ' ' + LEFT(#Remainder,1)
SET #Postcode = #District + ' ' + #Remainder
RETURN #Postcode
END
My attempt at creating a MYSQL version is below:
CREATE FUNCTION FullPostCodeFix (Postcode VARCHAR(20))
RETURNS VARCHAR(20)
BEGIN
DECLARE District Varchar(50)
DECLARE Remainder Varchar(50)
DECLARE Sector Varchar(50)
SET District= CASE
WHEN LEN(Postcode) - CHARINDEX(' ', REVERSE(Postcode)) = len(Postcode) THEN SUBSTRING(Postcode,1,(len(Postcode) - 3))
WHEN LEN(Postcode) - CHARINDEX(' ', REVERSE(Postcode)) < 3 THEN SUBSTRING(Postcode,1,(len(Postcode) - 3))
ELSE SUBSTRING(Postcode, 0, LEN(Postcode) - CHARINDEX(' ', REVERSE(Postcode)) + 1)
END
SET District = dbo.PostcodeFix(District)
SET Remainder= RIGHT(Postcode,3)
SET Sector = District + ' ' + LEFT(Remainder,1)
SET Postcode = District + ' ' + Remainder
RETURN Postcode
END
The error that I get is as follows:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE Remainder Varchar(50) DECLARE Sector Varchar(50)
There is another function called from within the function "FullPostCodeFix". This is my attempt:
DELIMITER $$
CREATE FUNCTION PostCodeFix (strDistrict VARCHAR(20))
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
DECLARE intASCII INTEGER;
SET strDistrict = LTRIM(strDistrict);
SET strDistrict = RTRIM(strDistrict);
IF LENGTH(strDistrict) > 4 OR LENGTH(strDistrict) = 0 THEN RETURN 'ERROR: ' + strDistrict;
ELSE
BEGIN
SET intASCII = ASCII(LEFT(strDistrict, 1));
IF ( intASCII > 47 AND intASCII < 58 ) THEN RETURN 'ERROR: ' + strDistrict;
ELSE
BEGIN
SET intASCII = ASCII(SUBSTRING(strDistrict, 2, 1));
IF ( intASCII > 47 AND intASCII < 58 ) THEN SET strDistrict = LEFT(strDistrict, 1) + ' ' + RIGHT(strDistrict, LENGTH(strDistrict)-1);
SET intASCII = ASCII(SUBSTRING(strDistrict, 3, 1));
IF ( intASCII < 48 OR intASCII > 57 ) AND (intASCII <> 32) THEN RETURN 'ERROR: ' + strDistrict;
ELSE IF LENGTH(strDistrict) < 4 THEN SET strDistrict = LEFT(strDistrict, 2) + ' ' + RIGHT(strDistrict, LENGTH(strDistrict)-2);
END IF;
END IF;
RETURN strDistrict;
END IF;
Ok lets start, replace MS-SQL functions with MySQL equivalent functions.
MSSQL MySQL
LEN() LENGTH()
SUBTRING() SUBSTR()
CHARINDEX() INSTR()
below is documentation and list of all MySQL String functions
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html
Here is correct MySQL Syntax that I have verified.
CREATE FUNCTION FullPostCodeFix (Postcode VARCHAR(20)) RETURNS VARCHAR(20)
BEGIN
DECLARE district VARCHAR(50);
DECLARE remainder VARCHAR(50);
DECLARE sector VARCHAR(50);
IF LENGTH(Postcode) - INSTR(' ', REVERSE(Postcode)) = LENGTH(Postcode) THEN SET district = SUBSTR(Postcode,1,(LENGTH(Postcode) - 3));
ELSEIF LENGTH(Postcode) - INSTR(' ', REVERSE(Postcode)) < 3 THEN SET district = SUBSTR(Postcode,1,(LENGTH(Postcode) - 3));
ELSE SET district = SUBSTR(Postcode, 0, LENGTH(Postcode) - INSTR(' ', REVERSE(Postcode)) + 1);
END IF;
SET District = dbo.PostcodeFix(District);
SET Remainder= RIGHT(Postcode,3);
SET Sector = CONCAT(District,' ',LEFT(Remainder,1));
SET Postcode = CONCAT(District,' ',Remainder);
RETURN Postcode;
END
Here is your second function
CREATE FUNCTION PostCodeFix (strDistrict VARCHAR(20))
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
DECLARE intASCII INTEGER;
SET strDistrict = LTRIM(strDistrict);
SET strDistrict = RTRIM(strDistrict);
IF LENGTH(strDistrict) > 4 OR LENGTH(strDistrict) = 0 THEN RETURN 'ERROR: ' + strDistrict;
ELSE
SET intASCII = ASCII(LEFT(strDistrict, 1));
IF ( intASCII > 47 AND intASCII < 58 ) THEN RETURN 'ERROR: ' + strDistrict;
ELSE
SET intASCII = ASCII(SUBSTRING(strDistrict, 2, 1));
IF ( intASCII > 47 AND intASCII < 58 ) THEN SET strDistrict = LEFT(strDistrict, 1) + ' ' + RIGHT(strDistrict, LENGTH(strDistrict)-1);
END IF;
SET intASCII = ASCII(SUBSTRING(strDistrict, 3, 1));
IF ( intASCII < 48 OR intASCII > 57 ) AND (intASCII <> 32) THEN RETURN 'ERROR: ' + strDistrict;
ELSEIF LENGTH(strDistrict) < 4 THEN SET strDistrict = LEFT(strDistrict, 2) + ' ' + RIGHT(strDistrict, LENGTH(strDistrict)-2);
END IF;
END IF;
RETURN strDistrict;
END IF;
END

MS-SQL stored procedure to MySQL

can anybody help me to write the following MS-SQL sp to MySQL sp,
use of this:
CREATE PROCEDURE sp_InputWork
#_DelimitedString nvarchar(MAX)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #_DataRow nvarchar(MAX)
DECLARE #_DescriptionOfWorkDone nvarchar(MAX)
DECLARE #_TemporaryStorage nvarchar(MAX)
DECLARE #_QTY int
DECLARE #_Total int
DECLARE #_CurrentField int
WHILE CHARINDEX(';', #_DelimitedString) > 0
BEGIN
SET #_DataRow = CAST(SUBSTRING(#_DelimitedString, 0, CHARINDEX(';', #_DelimitedString)) AS nvarchar(MAX))
SET #_CurrentField = 1
WHILE CHARINDEX(',', #_DataRow) > 0
BEGIN
SET #_TemporaryStorage = CAST(SUBSTRING(#_DataRow, 0, CHARINDEX(',', #_DataRow)) AS nvarchar(MAX))
IF #_CurrentField = 1
SET #_QTY = CAST(#_TemporaryStorage AS int)
IF #_CurrentField = 2
SET #_DescriptionOfWorkDone = #_TemporaryStorage
IF #_CurrentField = 3
SET #_Total = CAST(#_TemporaryStorage AS int)
SET #_DataRow = SUBSTRING(#_DataRow, CHARINDEX(',', #_DataRow) + 1, LEN(#_DataRow))
SET #_CurrentField = #_CurrentField + 1
END
INSERT INTO tblWorkDone (QTY, DescriptionOfWorkDone, Total) VALUES (#_QTY, #_DescriptionOfWorkDone, #_Total)
SET #_DelimitedString = SUBSTRING(#_DelimitedString, CHARINDEX(';', #_DelimitedString) + 1, LEN(#_DelimitedString))
END
END
I have changed the SP to Mysql
BEGIN
DECLARE _DataRow VARCHAR(21000);
DECLARE _DescriptionOfWorkDone VARCHAR(21000);
DECLARE _TemporaryStorage VARCHAR(21000) ;
DECLARE _QTY int;
DECLARE _Total int;
DECLARE _CurrentField int;
WHILE INSTR(_DelimitedString, ';') >0 DO
SET _DataRow = CAST(SUBSTRING(_DelimitedString, 1, INSTR(_DelimitedString,';' )) AS CHAR);
SET _CurrentField = 1;
WHILE INSTR(_DataRow,',' ) > 0 DO
SET _TemporaryStorage = CAST(SUBSTRING(_DataRow, 1, INSTR(_DataRow,',' )-1) AS CHAR);
IF _CurrentField = 1 then
SET _QTY = CAST(_TemporaryStorage AS UNSIGNED);
end if;
IF _CurrentField = 2 then
SET _DescriptionOfWorkDone = _TemporaryStorage;
end if;
IF _CurrentField = 3 then
SET _Total = CAST(_TemporaryStorage AS UNSIGNED) ;
end if;
SET _DataRow = SUBSTRING(_DataRow, INSTR( _DataRow,',') + 1, LENGTH(_DataRow));
SET _CurrentField = _CurrentField + 1;
END while;
INSERT INTO tblWorkDone (QTY, DescriptionOfWorkDone, Total) VALUES (_QTY, _DescriptionOfWorkDone, _Total);
SET _DelimitedString = SUBSTRING(_DelimitedString, INSTR(_DelimitedString,';' ) + 1, LENGTH(_DelimitedString));
END WHILE;END
Data inserted
CALL `sp_InputWork`('1,TEST,100,;2,TEST1,200,;3,TEST3,300,;')
Successfully looped and inserted to Mysql Table

T-SQL append number to variable

I couldn't find an answer to this....
I have three variables and need to switch between them in While Loop
Example:
DECLARE
#tableHTML NVARCHAR(MAX),
#email nvarchar(100),
#text1 nvarchar(100),
#text2 nvarchar(100),
#text3 nvarchar(100),
#number_suffix nvarchar(1)
SET #text1 = 'State of orders for Morfeus'
SET #text2 = 'State of orders for Fenix'
SET #text3 = 'State of orders for Perseus'
SET #number_suffix = 1
WHILE (#number_suffix < 4)
BEGIN
print #text(#number_suffix) /*and here is the problem */
SET #number_suffix = (#number_suffix + 1)
END
How do I append the number to variable #text please?
I am using MS SQL 2008
Do you want to append number to variable name? This is not possible. Why you need that, perhaps solution is more straightforward....
Try out following, perhaps id does what are you looking for:
DECLARE #cities TABLE(id int IDENTITY(1,1), cityName varchar(100))
INSERT INTO #cities(cityName) VALUES ('Morfeus'), ('Fenix'), ('Morfeus')
SELECT 'State of orders for ' + cityName
FROM #cities
Output:
State of orders for Morfeus
State of orders for Fenix
State of orders for Morfeus
To print number as well:
SELECT '#' + CAST(id AS varchar(2)) + ' State of orders for ' + cityName
FROM #cities
Output:
1 State of orders for Morfeus
2 State of orders for Fenix
3 State of orders for Morfeus
The question is not quite clear what your end game is but if I understand you correctly then something like this should work (note, you need to create the parse function first):
CREATE FUNCTION [dbo].[fnParseString]
(
#Section SMALLINT,
#Delimiter CHAR,
#Text VARCHAR(MAX)
)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #startindex NUMERIC(18,0),
#length NUMERIC(18,0),
#FieldPosition INT
SET #FieldPosition = ABS(#Section) - 1
SET #startindex = 0
WHILE #FieldPosition != 0
BEGIN
SET #FieldPosition = #FieldPosition - 1
SET #startindex = CHARINDEX(#Delimiter, #Text, #startindex + 1)
END
SET #Text = SUBSTRING(#Text, #startindex + 1, LEN(#Text) - #startindex)
SET #Text = SUBSTRING(#Text, 0, CHARINDEX(#Delimiter, #Text))
RETURN #Text
END
GO
DECLARE
#tableHTML NVARCHAR(MAX),
#email nvarchar(100),
#text nvarchar(100),
#number_suffix nvarchar(1)
SET #text = 'State of orders for Morfeus|State of orders for Fenix|State of orders for Perseus|'
SET #number_suffix = 1
WHILE (#number_suffix < 4)
BEGIN
PRINT dbo.fnParseString(#number_suffix, '|', #text)
SET #number_suffix = (#number_suffix + 1)
END
You can do it with Dynamic SQL but you'd need to reinitialize all you variables. The following will do it, but its a colossally bad idea, and you should use sll's answer instead
DECLARE
#tableHTML NVARCHAR(MAX),
#email nvarchar(100),
#number_suffix nvarchar(1),
#SQL nvarchar(max)
SET #number_suffix = 1
WHILE (#number_suffix < 4)
BEGIN
SET #SQL = N'
DECLARE #text1 nvarchar(100),
#text2 nvarchar(100),
#text3 nvarchar(100)
SET #text1 = ' + '''' + 'State of orders for Morfeus' + '''' +
'SET #text2 = ' + '''' + 'State of orders for Fenix' + '''' +
'SET #text3 = ' + '''' + 'State of orders for Perseus' + ''''
+
'PRINT #text' + #number_suffix
EXEC sp_executeSQL #SQL
SET #number_suffix = (#number_suffix + 1)
END