I am trying to create a search function in mysql. In order to make the search result more reliability i need to compare two string by words. Inputs are 2 strings and output is number word two strings match. In MySql i did as below.
CREATE DEFINER=`root`#`localhost` FUNCTION `CompareStrings`(str1 VARCHAR(255),str2 VARCHAR(255)) RETURNS double
BEGIN
DECLARE cur_position INT DEFAULT 1 ;
DECLARE remainder TEXT;
DECLARE cur_string VARCHAR(50);
DECLARE delimiter_length TINYINT UNSIGNED;
DECLARE numberMatch INT;
DECLARE total INT;
DECLARE result DOUBLE DEFAULT 0;
DECLARE delim VARCHAR(10);
DECLARE string2 VARCHAR(255);
SET delim = ' ';
DROP TEMPORARY TABLE IF EXISTS SplitString1;
CREATE TEMPORARY TABLE SplitString1 (
SplitString1ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT ,
val VARCHAR(50) NOT NULL
) ENGINE=MyISAM;
DROP TEMPORARY TABLE IF EXISTS SplitString2;
CREATE TEMPORARY TABLE SplitString2 (
SplitString1ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT ,
val VARCHAR(50) NOT NULL
) ENGINE=MyISAM;
SET remainder = str1;
SET delimiter_length = CHAR_LENGTH(delim);
WHILE CHAR_LENGTH(remainder) > 0 AND cur_position > 0 DO
SET cur_position = INSTR(remainder, delim);
IF cur_position = 0 THEN
SET cur_string = remainder;
ELSE
SET cur_string = LEFT(remainder, cur_position - 1);
END IF;
IF TRIM(cur_string) != '' THEN
INSERT INTO SplitString1(val) VALUES (cur_string);
END IF;
SET remainder = SUBSTRING(remainder, cur_position + delimiter_length);
END WHILE;
SET remainder = str2;
SET cur_position = 1;
WHILE CHAR_LENGTH(remainder) > 0 AND cur_position > 0 DO
SET cur_position = INSTR(remainder, delim);
IF cur_position = 0 THEN
SET cur_string = remainder;
ELSE
SET cur_string = LEFT(remainder, cur_position - 1);
END IF;
IF TRIM(cur_string) != '' THEN
INSERT INTO SplitString2(val) VALUES (cur_string);
END IF;
SET remainder = SUBSTRING(remainder, cur_position + delimiter_length);
END WHILE;
SELECT count(*) INTO numberMatch
FROM SplitString1 s1 JOIN SplitString2 s2 ON s1.val = s2.val;
RETURN result;
END
The idea is create two temporary table store each word and then compare these 2 tables. The result is good but the performace is awful. Anybody has better idea, please give me an advice.
Many thanks!
I don't think this will work as stated.
The logic is sound but you have not assigned any value to your result variable. Hence this function will always return 0. Replace:
RETURN result;
with
RETURN numberMatch;
Also replace:
CREATE DEFINER=`root`#`localhost` FUNCTION `CompareStrings`(str1 VARCHAR(255),str2 VARCHAR(255)) RETURNS double
with
CREATE DEFINER=`root`#`localhost` FUNCTION `CompareStrings`(str1 VARCHAR(255),str2 VARCHAR(255)) RETURNS double READS SQL DATA
As far as efficiency goes it looks pretty efficient. When you say 'performance is awful' - what constitutes 'awful'? Have you got any benchmark figures e.g. x calls took y millis?
Related
I have a table with following prototype:
CREATE TABLE `T2` (
`ID` int(11) DEFAULT NULL,
`NAME` varchar(100) DEFAULT NULL
)
I want to create procedure to insert random strings with a specific length and I wrote the following:
DELIMITER ;;
CREATE PROCEDURE STRGEN ( IN ITER INT) BEGIN
DECLARE COUNTER INT DEFAULT 0;
DECLARE _STR VARCHAR(1000) DEFAULT NULL;
WHILE COUNTER < #ITER DO
SET _STR = CONCAT(_STR , CHAR(RAND()*100+65));
SET COUNTER = COUNTER + 1;
END WHILE;
INSERT INTO T2 VALUES(RAND()*100 , _STR);
END;;
And called it this way:
CALL STRGEN(100);
The problem is that it inserts NULL as the NAME field.
My suspicion is that changes made to _STR goes out of scope when the while loop terminates.
I've also tried using a global variable like the following:
CREATE PROCEDURE STRGEN ( IN ITER INT) BEGIN
DECLARE COUNTER INT DEFAULT 0;
DECLARE _STR VARCHAR(1000) DEFAULT NULL;
SET #S = NULL;
WHILE (COUNTER < #ITER) DO
SET _STR = CONCAT(_STR , CHAR(RAND()*100+65));
SET COUNTER = COUNTER + 1;
SET #S = _STR;
END WHILE;
INSERT INTO T2 VALUES(RAND()*100 , #S);
END;;
And also declaring #S outside of the procedure , but nothing special happened.
Any help is appreciated.
Best regards.
When I tried running your procedure, I got the following error:
Error Code: 1366. Incorrect string value: '\x87' for column '_STR' at
row 1 0.000 sec
It seems that you are not generating valid chars... I tried a different char generator logic and it works as expected.
DELIMITER ;;
CREATE PROCEDURE STRGEN ( IN ITER INT) BEGIN
DECLARE COUNTER INT DEFAULT 0;
DECLARE _STR VARCHAR(1000) DEFAULT ''; -- change the initial value to ''
WHILE COUNTER < #ITER DO
-- SET _STR = CONCAT(_STR , CHAR(RAND()*100+65));
-- if I change the logic for generating random char, then I get expected result
SET _STR = CONCAT(_STR , LEFT(MD5(RAND()), 1));
SET COUNTER = COUNTER + 1;
END WHILE;
INSERT INTO T2 VALUES(RAND()*100 , _STR);
END;;
I have created a function on a MS SQL Server 2000 database that converts a base 10 number to base 64 and works fine for my purposes. I also need this function to be in a MySQL database I have converted it however it throws an exception saying that
Truncated double value 'B'
for example an example, now if I keep the number below 64 it converts it fine.
SQL function
CREATE FUNCTION ToBase64(#value int)
RETURNS varchar(50)
AS
BEGIN
DECLARE #seq char(64)
DECLARE #result varchar(50)
DECLARE #digit char(1)
SET #seq = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
SET #result = SUBSTRING(#seq, (#value%64)+1, 1)
WHILE #value > 0
BEGIN
SET #digit = SUBSTRING(#seq, ((#value/64)%64)+1, 1)
SET #value = #value/64
IF #value <> 0 SET #result = #digit + #result
END
RETURN #result
END
GO
mySQL function
DELIMITER $$
CREATE FUNCTION ToBase64( Pvalue int) RETURNS varchar(50)
DETERMINISTIC
BEGIN
DECLARE seq char(64);
DECLARE result varchar(50);
DECLARE digit char(1);
SET seq = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
SET result = SUBSTRING(seq, (Pvalue%64)+1, 1);
WHILE Pvalue > 0 do
SET digit = SUBSTRING(seq, ((Pvalue/64)%64)+1, 1);
SET Pvalue = Pvalue/64;
IF Pvalue <> 0 THEN
SET result = digit + result;
END IF;
End While;
RETURN (result);
END
This may be your problem:
IF Pvalue <> 0 THEN
SET result = digit + result;
END IF;
MySQL uses CONCAT() for string concatenation:
IF Pvalue <> 0 THEN
SET result = CONCAT(digit, result);
END IF;
I have created mysql function to split the string in rows using temporary table, it gives correct in temp table, but not when i call the function it returns 1 as output.
My code is as below
DELIMITER $$
DROP FUNCTION IF EXISTS `split`$$
CREATE FUNCTION `split`(`String` VARCHAR(8000), `Delimiter` CHAR(1)) RETURNS VARCHAR(8000) CHARSET latin1
BEGIN
DECLARE idx INT;
DECLARE slice VARCHAR(8000);
DECLARE result INT DEFAULT 0;
DROP TEMPORARY TABLE IF EXISTS tmp;
CREATE TEMPORARY TABLE `tmp` (items VARCHAR(8000));
-- set #tmptab = tmp;
SET idx = 1;
label: WHILE LENGTH(`String`)<1 OR `String` IS NULL DO
LEAVE label;
END WHILE;
loop_lable: WHILE (idx<>0) DO
SET idx = LOCATE(`Delimiter`,`String`);
IF (idx<>0) THEN
SET slice = LEFT(`String`,idx - 1);
ELSE
SET slice = `String`;
END IF;
IF(LENGTH(slice)>0) THEN
INSERT INTO tmp(Items) VALUES(slice) ;
SET result = 1;
END IF;
SET `String` = RIGHT(`String`,LENGTH(`String`) - idx);
IF LENGTH(`String`) = 0 THEN
LEAVE loop_lable;
END IF;
END WHILE;
RETURN result;
END$$
DELIMITER ;
This procedure does not return 2, it always returns 1. How to return 1 when insert happens, and return 2 when an update happens?
It is perfectly insert and update but return value always 1.
CREATE PROCEDURE [dbo].[sp_SaveEmployeeDetails]
#employeeID int
,#first_name varchar(255)
,#middle_name varchar(255)
,#last_name varchar(255)
,#gender varchar(255)
,#date_of_birth date
,#EntryBy varchar(255)
--,#ActionStatus int = NULL OUTPUT
AS
BEGIN
SET NOCOUNT OFF;
DECLARE #ActionStatus int;
IF NOT EXISTS(Select * From employees
Where employeeID =#employeeID)
BEGIN
INSERT INTO [dbo].[employees] ([first_name], [middle_name], [last_name], [gender], [date_of_birth], [EntryBy], [EntryDate])
VALUES (#first_name, #middle_name, #last_name, #gender, #date_of_birth, #EntryBy, getdate())
SET #ActionStatus = 1;
END
ELSE
BEGIN
UPDATE employees
SET [first_name] = #first_name,
[middle_name] = #middle_name,
[last_name] = #last_name,
[gender] = #gender,
[date_of_birth] = #date_of_birth,
[EntryBy] = #EntryBy,
[EntryDate] = getdate()
WHERE
employeeID = #employeeID
SET #ActionStatus = 2;
END
RETURN #ActionStatus;
--also try SELECT #ActionStatus;
END
The value you return here
RETURN #ActionStatus;
...might be set to a variable directly, its the procedure's "own" return value. Look at this example:
CREATE PROCEDURE dbo.TestReturn(#IntValue INT)
AS
BEGIN
IF #IntValue=0
RETURN 0;
ELSE
RETURN 1;
END
GO
DECLARE #RetVal INT;
EXEC #RetVal = dbo.TestReturn -1;
SELECT #RetVal;
EXEC #RetVal = dbo.TestReturn 0;
SELECT #RetVal;
EXEC #RetVal = dbo.TestReturn 1;
SELECT #RetVal;
GO
DROP PROCEDURE dbo.TestReturn;
It comes back with 1-0-1 as expected.
But this return value is bound to INT and is meant to reflect execution states (like error codes, success level...) In your case this seems to be the case...
If you think about output of any other data use OUTPUT parameters.
CREATE PROCEDURE dbo.TestReturn(#IntValue INT, #TestPrm VARCHAR(100) OUTPUT)
AS
BEGIN
IF #IntValue=0
BEGIN
SET #TestPrm='Value was 0'
RETURN 0;
END
ELSE
BEGIN
SET #TestPrm='Value was something else';
RETURN 1;
END
END
GO
DECLARE #RetVal INT;
DECLARE #RetVarchar VARCHAR(100);
EXEC #RetVal = dbo.TestReturn -1,#RetVarchar OUTPUT;
SELECT #RetVal,#RetVarchar;
EXEC #RetVal = dbo.TestReturn 0,#RetVarchar OUTPUT;
SELECT #RetVal,#RetVarchar;
EXEC #RetVal = dbo.TestReturn 1,#RetVarchar OUTPUT;
SELECT #RetVal,#RetVarchar;
GO
DROP PROCEDURE dbo.TestReturn;
I have a MySQL variable as below.
DECLARE str TEXT DEFAULT '2014-01-02 13:00:00|2014-02-04 12:59:59#0#2014-02-04 13:00:00|2014-03-04 12:59:59#0#2014-03-04 13:00:00|2014-04-02 13:59:59#0#2014-04-02 14:00:00|2014-05-02 14:59:59#0#2014-05-02 15:00:00|2014-06-03 14:59:59';
I want to break this whole string first by using the separator #0# and from the results break the string using separator |.
I have tried MySQL split_str function but I am not able to do it.
Its giving me the error split_str does not exist.
Please suggest some other way to do this.
Finally i have resolved my problem using below procedure.
I am able to solve it using temporary table and procedure. I am no more using mysql variable for this. We can call the procedure as below....
CALL SplitString('2014-01-02 13:00:00|2014-02-04 12:59:59#0#2014-02-04 13:00:00|2014-03-04 12:59:59#0#2014-03-04 13:00:00|2014-04-02 13:59:59#0#2014-04-02 14:00:00|2014-05-02 14:59:59#0#2014-05-02 15:00:00|2014-06-03 14:59:59', '#0#', 'tblindex');
Here tblindex is temporary table i have used.
My procedure is below....
DELIMITER $$
DROP PROCEDURE IF EXISTS `SplitString`$$
CREATE PROCEDURE `SplitString`( IN input TEXT,IN delm VARCHAR(10),tblnm varchar(50))
BEGIN
DECLARE cur_position INT DEFAULT 1 ;
DECLARE remainder TEXT;
DECLARE cur_string VARCHAR(1000);
DECLARE delm_length TINYINT UNSIGNED;
set #sql_drop = concat("DROP TABLE IF EXISTS ",tblnm);
prepare st_drop from #sql_drop;
execute st_drop;
set #sql_create = concat("CREATE TEMPORARY TABLE ",tblnm," (value VARCHAR(2000) NOT NULL ) ENGINE=MEMORY;");
prepare st_create from #sql_create;
execute st_create;
SET remainder = input;
SET delm_length = CHAR_LENGTH(delm);
WHILE CHAR_LENGTH(remainder) > 0 AND cur_position > 0
DO
SET cur_position = INSTR(remainder, delm);
IF cur_position = 0 THEN
SET cur_string = remainder;
ELSE
SET cur_string = LEFT(remainder, cur_position - 1);
END IF;
-- select cur_string;
IF TRIM(cur_string) != '' THEN
set #sql_insert = concat("INSERT INTO ",tblnm," VALUES ('",cur_string,"');");
prepare st_insert from #sql_insert;
execute st_insert;
END IF;
SET remainder = SUBSTRING(remainder, cur_position + delm_length);
END WHILE;
END$$
DELIMITER ;