Use different DB in Stored Procedure of Mysql - mysql

Following is my Stored Procedure
DELIMITER $$
USE `us_db`$$
DROP PROCEDURE IF EXISTS `getTransactionDetails`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `getTransactionDetails`(IN _MainDB VARCHAR(32),
IN _mobile VARCHAR(50),
IN _market_id VARCHAR(50),
IN _from DATE,
IN _to DATE)
BEGIN
SELECT
transaction_id AS txn_id,
DATE_FORMAT(createdon,"%d-%b-%Y %H:%i:%S") AS createdon,
order_id,
order_conv_total AS amount,
msisdn AS msisdn,
channel_id,
transmode_id,
statusid,
(SELECT fullname FROM _MainDB.entities WHERE entity_id=retailer_id) AS retailer,
FROM `us_transaction` `t` WHERE
t.market_id = _market_id AND msisdn = _mobile AND t.createdon BETWEEN CONCAT(DATE(_from)," 00:00:00") AND CONCAT(DATE(_to)," 23:59:59") ORDER BY t.createdon DESC LIMIT 100;
END$$
DELIMITER ;
Now the issue is that the retailer name is stored in a separate DB which i want to call through _MainDB. This DB name is coming as an argument, but it's not coming properly because the _MainDB is considered as varchar type. How can I take the DB name as an argument in a stored procedure???

user variable and CONCAT function;
DECLARE STR VARCHAR(1000)
SET #STR = 'SELECT
transaction_id AS txn_id,
........
(SELECT fullname FROM
'
SET #STR = CONCAT(#STR,#_MAINDB)
SET #STR = CONCAT(#STR,'.entities ..................')
PREPARE STMT FROM #STR
EXECUTE STMT

Related

Insert each row of stored procedure into a table

I have two stored procedures in MySql. One of them does the calculation and returns everything in groups. With another stored procedure, I have a WHILE statement that runs for each month and calls the first stored procedure. I want to get the result of each group and save them in a table.
The code is something like:
CREATE PROCEDURE `first`(IN `start` date, **it should be list of items** )
begin
SET result = 0;
SELECT
item.name,
sum(amount) *INTO result*
FROM
FOO
INNER JOIN
BAR ON id = id
WHERE
date(someDate) < date(start)
group by something;
end
And the runner is something like:
CREATE PROCEDURE `runner`(IN `from` date, IN `to` date)
BEGIN
set dateTo = date(to);
set dateFrom = date(from);
WHILE DATE(dateFrom) <= DATE(dateTo) DO
call first(dateFrom, #res);
// here I need another loop through all results of the first procedure to insert each one of them in the following line.
insert into table_x (**some values which have been returned**);
SET dateFrom = DATE_ADD(dateFrom, INTERVAL 1 month);
END WHILE;
END
I don-' think that the loop and the second procedure ist really necessary
MySQL can't return table arrays or something of that kind, but you can use temporary tables
DELIMITER $$
CREATE PROCEDURE `first`(IN `start` date )
begin
DROP TEMPORARY TABLE IF EXISTS myTable;
CREATE TEMPORARY TABLE myTABLE
SELECT
item.name,
sum(amount)
FROM
FOO
INNER JOIN
BAR ON id = id
WHERE
date(someDate) < date(start)
group by something;
end$$
DELIMITER ;
Outer procdudre
DELIMITER $$
CREATE PROCEDURE `runner`(IN `_from` date, IN `_to` date)
BEGIN
set dateTo = date(_to);
set dateFrom = date(_from);
WHILE DATE(dateFrom) <= DATE(dateTo) DO
call first(dateFrom, #res);
insert into table_x (SELECT * FROM myTable);
SET dateFrom = DATE_ADD(dateFrom, INTERVAL 1 month);
END WHILE;
END$$
DELIMITER ;
You ca make dynamic sql work with different variables
DELIMITER $$
CREATE PROCEDURE `first`(IN `_start` date , IN _group varchar(100))
begin
DROP TEMPORARY TABLE IF EXISTS myTable;
SET #sql := CONCAT("
CREATE TEMPORARY TABLE myTABLE
SELECT
item.name,
sum(amount)
FROM
FOO
INNER JOIN
BAR ON id = id
WHERE
date(someDate) < date(",_gRoup,")
group by",_goup,";");
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
end$$
DELIMITER ;
This might work, if all ** some values ** are replaced.
Minimal version of MySQL needed is 8.0 (or an equivalent MariaDB version).
The two variables #from and #to are set to let this script know from which data to which date is needed.
This code is untested, because OP wrote "I have two stored procedures in MySql", but he uses things like "** some values **", which make the two stored procedures invalid (sigh).
Final remark: It is, of course, possible to wrap this complete statement into a stored procedure. (If above corrections are made)
set #from = '2021-05-01';
SET #to = '2021-06-13';
insert into table_x (** some values **)
WITH recursive dates AS (
SELECT #from as d
UNION ALL
SELECT date_add(d, INTERVAL 1 DAY)
FROM dates
WHERE d<#to
)
SELECT d, **some values which have been returned**
FROM dates
CROSS JOIN (
SELECT
item.name,
sum(amount)
FROM
FOO
INNER JOIN
BAR ON id = id
WHERE
date(someDate) < date(d)
group by something) sq

Select Query with Stored Procedure

I want to call a procedure with some conditions,
This is my code.
DELIMITER $$
USE `jijo_db`$$
DROP PROCEDURE IF EXISTS `view_all_user_details_with_limit`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `view_all_user_details_with_limit`(IN StartNo INT,IN EndNo INT, IN OrderVal VARCHAR(10),IN Cond VARCHAR(50))
BEGIN
SELECT * FROM `tbl_user_details` WHERE Cond ORDER BY OrderVal LIMIT StartNo,EndNo;
END$$
DELIMITER ;
procedure call - CALL view_all_user_details_with_limit(0,10,'',"NAME LIKE '%a%'");
but I dont get any result. why ????
If you want to have variable WHERE, LIMIT, and ORDER BY conditions, you will need to create a prepared statement in your stored procedure.
Try something like this:
DELIMITER $$
USE `jijo_db`$$
DROP PROCEDURE IF EXISTS `view_all_user_details_with_limit`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `view_all_user_details_with_limit`(IN StartNo INT,IN EndNo INT, IN OrderVal VARCHAR(10),IN Cond VARCHAR(50))
BEGIN
SET #q = CONCAT('SELECT * FROM `tbl_user_details` WHERE ', Cond);
IF OrderVal != '' THEN
SET #q = CONCAT(#q, ' ORDER BY ', OrderVal);
END IF;
SET #q = CONCAT(#q, ' LIMIT ', StartNo, ', ', EndNo - StartNo + 1);
PREPARE stmt FROM #q;
EXECUTE stmt;
END$$
DELIMITER ;
Your problem seems to be happening in WHERE Cond.
A MySQL Stored Procedure won't interpret that string as an expression, but will instead attempt to cast it to a Boolean.
You can see by running SELECT CAST("NAME LIKE '%a%'" AS UNSIGNED); that the string will be interpreted as False, and thereby you won't get any results.
I suggest instead that you accept in your Cond variable a string such as '%a%' and then update your query to:
SELECT *
FROM `tbl_user_details`
WHERE `NAME` LIKE Cond
ORDER BY OrderVal
LIMIT StartNo, EndNo;

Is it possible to pass a variable to a stored procedure call in mysql?

I am trying to do the following and I am wondering if that is possible in MySQL:
CREATE PROCEDURE `sp_test`(
ulon int(4),
usrname varchar(20),
usrst varchar(20),
usrdt varchar(10)
)
BEGIN
DECLARE bid int(11);
START TRANSACTION;
SELECT t_bid INTO bid FROM generalb WHERE dt=usrdt;
INSERT
INTO
rslt
(
rbid,
rusrname,
rusrst,
usrdt
)
VALUES
(
bid,
usrname,
usrst,
usrdt
);
call sp_nextproc_13(bid, ulon);
COMMIT;
END;
Look at this line:
call sp_nextproc_13(bid, ulon);
How could I achieve something like this:
call sp_nextproc_#bid(bid, ulon);
I need to be able to call a procedure dynamically as I do not know the name of the procedure until I get the bid. If anyone knows the answer, thanks so much
LATEST UPDATE:
I have made the following changes:
CREATE PROCEDURE `sp_test`(
ulon int(4),
usrname varchar(20),
usrst varchar(20),
usrdt varchar(10)
)
BEGIN
DECLARE bid int(11);
START TRANSACTION;
SELECT t_bid INTO bid FROM generalb WHERE dt=usrdt;
INSERT
INTO
rslt
(
rbid,
rusrname,
rusrst,
usrdt
)
VALUES
(
bid,
usrname,
usrst,
usrdt
);
SET #sql=concat('call sp_nextproc_',bid,'(?,?)');
PREPARE stmt FROM #sql;
SET #var1=bid;
SET #var2=ulon;
EXECUTE stmt USING #var1, #var2;
COMMIT;
END;
IF I hardcode some values, it works. However if I don't, it doesn't. Please see below:
If I call:
CALL sp_test(2, 'John','test','AAAA');
ERROR here:
SELECT t_bid INTO bid FROM generalb WHERE dt=usrdt;
Column t_bid cannot be null
If I hardcode the value like this:
SELECT t_bid INTO bid FROM generalb WHERE dt='AAAA';
The error disappears.
The second error is at the following line:
SET #var1=bid;
SET #var2=ulon;
EXECUTE stmt USING #var1, #var2;
ERROR missing value #var1 and #var2.
If I hardcode var1 and var2, it works:
SET #var1=13;
SET #var2=2;
EXECUTE stmt USING #var1, #var2;
I tried using # and without it on the variables but that didn't work. What I am doing wrong? :(
delimiter $$
create procedure sp_test
(
ulon int(4)
)
BEGIN
DECLARE bid int(11);
set bid=8888;
...
...
...
...
set #sql=concat('call sp_nextproc_',bid,'(?,?)');
prepare stmt from #sql;
set #var1=bid;
set #var2=ulon;
EXECUTE stmt using #var1,#var2;
END
$$
-- ------------
delimiter $$
CREATE PROCEDURE `sp_nextproc_8888`(
IN bid int(11),
IN ulon int(4)
)
BEGIN
select bid,ulon;
END
$$
-- test with these:
-- call sp_test(9999);
-- call sp_nextproc_8888(111,222);

Using LIKE in a stored procedure and escaping injection at same time

So I have a stored procedure in MySQL
delimiter //
DROP PROCEDURE IF EXISTS mysearch;
CREATE PROCEDURE mysearch (IN term varchar(255), IN brands varchar(512), IN categories varchar(512), IN priceRange varchar(32))
BEGIN
SET #query = 'SELECT name, price, image FROM (... join some tables here ... ) WHERE (... prod.category = some number ...)';
IF (term IS NOT NULL) THEN
SET term = CONCAT('\'%',term,'%\'');
SET #query = CONCAT(#query, ' AND (prod.name LIKE ', QUOTE(term), ' OR prod.number LIKE ', QUOTE(term), ')');
END IF;
PREPARE stmt1 FROM #query;
EXECUTE stmt1;
END //
delimiter ;
How can I use LIKE and still escape user input?
I would like to sanitize user input, but still allow search using LIKE.
Perhaps something like this
DROP PROCEDURE IF EXISTS mysearch;
CREATE PROCEDURE mysearch (IN term varchar(255), IN brands varchar(512), IN categories varchar(512), IN priceRange varchar(32))
BEGIN
DECLARE fullterm varchar(255);
SET fullterm = CONCAT('%',term,'%');
SELECT name, price, image FROM (... join some tables here ... ) WHERE (... prod.category = some number ...)
AND (term IS NULL OR (prod.name LIKE fullterm OR prod.number LIKE fullterm));
END
Demo at http://sqlfiddle.com/#!2/a5e50/9
You can do that by using PREPARE statement.
There is an example here.

What is the workaround for using dynamic SQL in a stored Procedure

The Stored Procedure
DELIMITER $$
CREATE PROCEDURE `lms`.`leads_to_bak` ()
BEGIN
SET #table1 = (SELECT `tabler_name` FROM `sets` WHERE `on_off`=0 LIMIT 1);
SET #table2 = CONCAT(#table1, '_bak');
SET #SQL1 = CONCAT('INSERT INTO ',#table2, '(', (SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME), 'lead_id,', '') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #table2), ')', ' SELECT ', (SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME), 'lead_id,', '') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #table1), ' FROM ', #table1);
PREPARE stmt FROM #sql1;
EXECUTE stmt;
END$$
DELIMITER ;
The Trigger
DELIMITER $$
USE `lms`$$
CREATE TRIGGER `lms`.`after_insert_into_leads`
AFTER INSERT ON `sets` FOR EACH ROW
BEGIN
CALL lms.leads_to_bak();
END$$
DELIMITER ;
The problem
I get a Error Code: 1336. Dynamic SQL is not allowed in stored function or trigger error message when making an INSERT which by implication would execute the trigger and the stored procedure. I am assuming the problem is the Dynamic SQL here:
PREPARE stmt FROM #sql1;
EXECUTE stmt;
I've looked around and there is a thread on stackoverflow on the problem, but no answer. Does anyone have any suggestions for a plausible workaround?
There is no good workaround for the absense of Dynamic SQL in MySQL functions, just klunky cludges. Some things still remain downright impossible to cludge, such as using a dynamically-calculated field name or table name in a SQL query. Yes, once in a while there is a need for doing this sort of thing!
And don't try cheat by putting the Dynamic SQL in a stored procedure and wrapping in a function or trigger, as the question poser tried - MySQL is too clever and will give you the usual obscure error message. Believe me, I have been around all the houses.
Coming from an Oracle PL/SQL and MS SQL Server background, I sorely miss the richness that PL/SQL and (to a small extent) T-SQL offers for writing procedural SQL.
Within the procedure definition, you need to store all your IN/OUT variables.
Change:
CREATE PROCEDURE `lms`.`leads_to_bak` ()
to:
CREATE PROCEDURE `lms`.`leads_to_bak` (
IN table1 varchar(32),
IN table2 varchar(32),
)
Then call doing this:
CALL `lms`.`leads_to_bak`('table1', 'table2')
replacing the strings with your own.
The purpose of using stored procedures is to prevent SQL injection using strictly typed data. You don't technically need to prepare it in the stored procedure if you ONLY send strictly typed input variables in the parameter list.
This way, you handle the string operations prior to the stored procedure call. Keep your stored procs skinny!
Here's an example of one of my stored procedures:
DELIMITER ;
DROP PROCEDURE IF EXISTS `save_player`;
DELIMITER //
CREATE PROCEDURE `save_player` (
IN uid int(15) UNSIGNED,
IN email varchar(100),
IN name varchar(100),
IN passwd char(96),
IN state ENUM('active','suspended','deleted'),
IN user_role ENUM('gamemaster','moderator','player'),
IN locale ENUM('en','fr'),
IN lvl tinyint(1),
IN hp bigint(20),
IN reborn tinyint(1),
IN cross_ref varchar(12),
IN email_verified tinyint(1),
OUT new_id int(15) UNSIGNED
)
BEGIN
DECLARE date_deleted timestamp DEFAULT NULL;
IF uid > 0 AND EXISTS (SELECT id FROM user WHERE `id`= uid) THEN
IF state = 'deleted' THEN
SET date_deleted = CURRENT_TIMESTAMP;
END IF ;
UPDATE `user` SET
`email` = email,
`name` = name,
`passwd` = passwd,
`state` = state,
`user_role` = user_role,
`locale` = locale,
`lvl` = lvl,
`hp` = hp,
`reborn` = reborn,
`cross_ref` = cross_ref,
`email_verified` = email_verified,
`date_deleted` = date_deleted
WHERE `id` = uid;
SET new_id = uid;
ELSE
INSERT INTO user (`email`, `name`, `passwd`, `state`, `user_role`, `locale`, `lvl`, `hp`, `reborn`, `cross_ref`, `email_verified`, `date_created`)
VALUES (email, name, passwd, state, user_role, locale, lvl, hp, reborn, cross_ref, email_verified, NOW());
SELECT LAST_INSERT_ID() INTO new_id;
END IF;
END //
DELIMITER ;