Mysql Stored Procedure Ignores Parameters and WHERE conditions - mysql

I have the following which works great:
SET #param = '%string%';
SELECT *
FROM tbl
WHERE IF(#param IS NOT NULL, LIKE #param, TRUE);
However when I change to this:
DELIMITER //
CREATE PROCEDURE test(IN param VARCHAR(255))
BEGIN
SET #param = param;
SELECT *
FROM tbl
WHERE IF(#param IS NOT NULL, LIKE #param, TRUE);
END //
DELIMITER ;
CALL test('%string%');
All rows are returned as if there were no parameter passed in.
I assume that there is some limitation or restriction with MySQL that I am missing?

Not sure but please try this with prepared statement
DELIMITER //
CREATE PROCEDURE test(IN param VARCHAR(255))
BEGIN
SET #param = param;
SET #q = CONCAT("SELECT *
FROM tbl
WHERE IF(",#param," IS NOT NULL, LIKE ",#param,", TRUE)");
PREPARE stmt FROM #q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;

This statement is syntactically wrong and won't get compiled:
WHERE IF(#param IS NOT NULL, LIKE #param, TRUE);
and you can't just place variable values like that to construct an SQL statement.
You need a Prepared Statement to prepare and execute such statements.
Try the following: (you need to replace column_name with valid column name from your table.
DELIMITER //
DROP PROCEDURE IF EXISTS test //
CREATE PROCEDURE test( IN param VARCHAR( 255 ) )
BEGIN
SET #param = param;
SET #query = CONCAT( 'SELECT * FROM tbl WHERE column_name LIKE ' );
SET #query = CONCAT( #query, ' IF( \'', #param, '\' IS NOT NULL, \'' );
SET #query = CONCAT( #query, #param, '\', TRUE )' );
PREPARE stmt FROM #query;
EXECUTE stmt;
DROP PREPARE stmt; -- deallocate prepare stmt;
END;
//
DELIMITER ;
CALL test('%string%');
If you run select #query, it will return following statement:
SELECT * FROM tbl
WHERE column_name LIKE IF( '%string%' IS NOT NULL, '%string%', TRUE )

Related

Do something if a table exists

I am trying to find out if there a table called OOK and if so, do something to it. This is what I have so far, which does not work with a helpful ERROR 1064 […] syntax error message:
IF show tables like 'OOK' THEN
DELETE FROM OOK WHERE Id = 'Development';
INSERT INTO OOK VALUES ( 'Development', 'Localhost' );
END IF
This is to support some legacy code and might not be the best solution to the problem. However, it will fix it for what I need.
Since I am getting lots of syntax errors on the answers, here is the exact version I have: Server version: 5.5.60-MariaDB MariaDB Server.
You can access Information Schema, to check if the table exists or not. Also, you will need to use Dynamic SQL (to handle the case when table name does not exist)
Try something like below:
IF EXISTS (SELECT 1
FROM information_schema.tables
WHERE table_schema = 'your_database_name'
AND table_name = 'OOK') THEN
SET #s1 = 'DELETE FROM your_database_name.OOK WHERE Id = ?';
SET #a = 'Development';
PREPARE stmt1 FROM #s1;
EXECUTE stmt1 USING #a;
DEALLOCATE PREPARE stmt1;
SET #s2 = 'INSERT INTO your_database_name.OOK VALUES (?, ?)';
SET #b = 'Development';
SET #c = 'Localhost';
PREPARE stmt2 FROM #s2;
EXECUTE stmt1 USING #b, #c;
DEALLOCATE PREPARE stmt2;
END IF
I use the procedure and the function below in a table duplication process in a db:
Usage
CALL do_something('mydb', 'OOK', 'Development', 'Development', 'Localhost');
Implementation
DELIMITER $$;
CREATE PROCEDURE do_something(
IN dbName VARCHAR(255),
IN tableName VARCHAR(255),
IN id VARCHAR(255),
IN value1 VARCHAR(255),
IN value2 VARCHAR(255)
)
BEGIN
IF ( fn_table_exists(dbName, tableName) )
THEN
CALL statement(CONCAT(
'DELETE FROM ', tableName, ' WHERE Id = "', id, '"'));
CALL statement(CONCAT(
'INSERT INTO ', tableName, ' VALUES ( "', value1, '", "', value2, '" )'));
ELSE
SELECT CONCAT(
'ERROR: Table "', tableName, '" does not exist in the schema "', dbName, '".'
) AS ErrorMessage;
END IF;
END$$
DELIMITER ;
DELIMITER $$;
CREATE PROCEDURE statement(IN dynamic_statement TEXT)
BEGIN
SET #dynamic_statement := dynamic_statement;
PREPARE prepared_statement FROM #dynamic_statement;
EXECUTE prepared_statement;
DEALLOCATE PREPARE prepared_statement;
END$$
DELIMITER ;
DELIMITER $$;
CREATE FUNCTION fn_table_exists(dbName VARCHAR(255), tableName VARCHAR(255))
RETURNS TINYINT(1)
BEGIN
DECLARE totalTablesCount INT DEFAULT (
SELECT COUNT(*)
FROM information_schema.TABLES
WHERE (TABLE_SCHEMA COLLATE utf8_general_ci = dbName COLLATE utf8_general_ci)
AND (TABLE_NAME COLLATE utf8_general_ci = tableName COLLATE utf8_general_ci)
);
RETURN IF(
totalTablesCount > 0,
TRUE,
FALSE
);
END$$
DELIMITER ;

How to convert mysql/mariadb row in triggers (NEW/OLD) into json

how i can convert a row inside a mysql/mariadb trigger into an json object with new JSON features?
BEGIN
CALL my_audit_insert(tableName, id, ... JSON_OBJECT(NEW) ...);
END
Is there any possibility to get programatically columns of NEW or OLD?
First Try - Create a Statement
Idea is to get colums from system tables and get each value from NEW/OLD programatically
BEGIN
SET #s = 'SELECT NEW.? INTO #result';
PREPARE stmt FROM #s;
SET #a = 'id';
EXECUTE stmt USING #a;
CALL audit_insert(NEW.id, 'pages', JSON_ARRAY(result));
END
(1336): Dynamic SQL is not allowed in stored function or trigger
Second Idea - Select the row via PrimaryKey as JSON_Object in after-triggers
procedure spGetJson from https://stackoverflow.com/a/35957518/7080961
DROP PROCEDURE IF EXISTS `spGetJson`;
DELIMITER //
CREATE DEFINER=`root`#`%` PROCEDURE `spGetJson`(pTableName varchar(45), pId int, out pJson JSON)
begin
select group_concat(concat("'", COLUMN_NAME, "', ", COLUMN_NAME) separator ',')
into #cols
from information_schema.columns
where TABLE_NAME = pTableName and TABLE_SCHEMA = database();
set #q = concat('select json_object(', #cols, ') INTO #a from ', pTableName);
if pId is not null then
set #q = concat(#q, ' where id = ', pId);
end if;
set #q = concat(#q, ';');
prepare statement from #q;
execute statement;
deallocate prepare statement;
SET pJson = #a;
end//
DELIMITER;
After Insert Trigger:
BEGIN
CALL spGetJson('pages', NEW.id, #a);
CALL audit_insert(NEW.id, 'pages', #a);
END
same: (1336): Dynamic SQL is not allowed in stored function or trigger
Conclusion:
have to wait for this feature: https://bugs.mysql.com/bug.php?id=89366
or switch to postresql

Stored procedure output variable

I am trying to run a procedure that takes a parameter 'table' for the query, and result as the output parameter. However, it shows as undeclared variable: result
I have doubled checked that no spelling mistake but still have no idea how it happened. Would someone please provide some help or guidance
CREATE DEFINER=`root`#`localhost` PROCEDURE `Function`(IN table varchar(10), OUT result varchar (10))
BEGIN
SET #q = CONCAT ('
Select `field` from `',table,'` into result limit 1;');
PREPARE stmt from #q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
Try:
...
-- SET #q = CONCAT ('Select `field` from `',table,'` into result limit 1;');
SET #`q` := CONCAT('SELECT `der`.`field`
FROM (SELECT `field` FROM `', `table`, '` LIMIT 1) `der`,
(SELECT #`result` := NULL) `init`
INTO #`result`;');
PREPARE `stmt` from #`q`;
EXECUTE `stmt`;
SET `result` := #`result`;
DEALLOCATE PREPARE `stmt`;
...
It is important to indicate the difference between 9.4. User-Defined Variables and routine parameters 13.1.15. CREATE PROCEDURE and CREATE FUNCTION Syntax, are different variables.
SQL Fiddle demo

how to set value to output parameter in dynamic sql procedure?

I have tried with following code. But it shows error msg like this undeclared variable :nt.
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_ntime`(in tb varchar(50),in d int, out nt varchar(50))
BEGIN
SET #statment = concat('Select ntime into nt from ',tb);
SET #date = CONCAT(' WHERE date = "', d, '"');
SET #statmen = CONCAT(#statment, #date);
PREPARE stmt FROM #statment;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
When used within single quotes nt will not be treated as variable but a literal.
Use local variable to set value into and assign the same to out param after execution.
Example:
DELIMITER //
CREATE DEFINER=`root`#`localhost`
PROCEDURE `get_ntime`( in tb varchar(50), in d int, out nt varchar(50) )
BEGIN
SET #staetment = concat( 'Select ntime into #nTime from ', tb );
-- SET #date = CONCAT( ' WHERE date = "', d, '"' );
SET #date = CONCAT( ' WHERE date = ?' );
SET #statement = CONCAT( #statement, #date );
SET #dt := d;
PREPARE stmt FROM #statement;
-- EXECUTE stmt;
EXECUTE stmt using #dt;
DEALLOCATE PREPARE stmt;
SET nt := #nTime;
END;
//
DELIMITER ;
Prepared statements have session scope, mysql doesn't know you want to use your prepared statement inside stored procedure only. You deallocate the statement immediately, but it doesn't have to be always like that.
Thats why mysql simply disallows using anything that has less scope inside your prepared statement - as is the case with in and out parameters, which have a scope of stored procedure.
As a workaround mentioned in mysql prepare statement manual you can use user variable inside your prepared statement and then SET your out paremeter to that user variable.

MySql Stored Procedure and Comma Separated Varchar with In Statement

I'm trying to create a stored procedure which will take a comma separated list as a value and push that into a select statement with an IN clause. I'm aware of find_in_set which works, but does have a performance overhead. I've also trialled a prepare statement but the problem remains.
My Example:
My parameter is sp1 and contains the value: 'valuex, valuey, valuez'.
BEGIN
set #sql = concat('select * from tablename WHERE assignedTo in (', sp1, ')');
PREPARE q FROM #sql;
execute q;
END
As expected, this throws an error since SQL will treat the value as column names. My question is how do I achieve: 'valuex','valuey','valuez' FROM 'valuex, valuey, valuez'?
You can use replace function:
BEGIN
set #sql = concat("select * from tablename WHERE assignedTo in ('", replace(sp1,",","','"), "')");
PREPARE q FROM #sql;
execute q;
END
An option is:
DELIMITER $$
DROP PROCEDURE IF EXISTS `sp_test`$$
CREATE PROCEDURE `sp_test`(`sp1` VARCHAR(50))
BEGIN
SET `sp1` := REPLACE(REPLACE(`sp1`, ', ', ','), ',', '\',\'');
SET #`query` := CONCAT('SELECT `column0`, `column1` FROM `tablename` WHERE `assignedTo` IN (\'', `sp1`, '\');');
PREPARE `stmt` FROM #`query`;
EXECUTE `stmt`;
DEALLOCATE PREPARE `stmt`;
END$$
DELIMITER ;
CALL `sp_test`('valuex, valuey, valuez');
-- SELECT `column0`, `column1` FROM `tablename` WHERE `assignedTo` IN ('valuex','valuey','valuez');