Creating map and use that within the MySQL stored procedure - mysql

I am working on a migration project and the data needs to be migrated from system A to system B. The id's in system A and system B will be different. So maintaining a table with source and target column.
As part of the migration, For each record from source A, I need to go to the mapping table to find the corresponding target value. I am just trying avoid querying the mapping table for each query. Is there any way to store the mapping table as a key value map in a stored procedure and query the map as opposed to going to the table directly?
I was searching for collections in MySQL stored procedure, but didn't find anything.
Database A:-
Table : employee
---------------------------------
id | name | email |
---------------------------------
10021 | First1 | first#test.com |
---------------------------------
10035 | First 2| first#test.com |
---------------------------------
Database B:-
Table : employee
---------------------------------
id | name | email |
---------------------------------
1 | First1 | first#test.com |
---------------------------------
2 | First 2| first#test.com |
---------------------------------
Table : emp_mapping
----------------------
source_id | target_id|
----------------------
10021 | 1 |
----------------------
10035 | 2 |
----------------------
Stored Procedure for Migration
DELIMITER //
CREATE PROCEDURE LoadEmployees()
BEGIN
DECLARE db1_empl_id INT;
DECLARE db1_empl_name VARCHAR(20);
DECLARE db1_empl_email VARCHAR(40);
DECLARE db2_empl_id INT;
DECLARE cur CURSOR FOR SELECT id, name, email FROM db1.employees;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET cursor_finished = 1;
open cur;
cur_loop: LOOP
FETCH cur INTO db1_empl_id, db1_empl_name, db1_empl_email;
IF cursor_finished = 1 THEN
LEAVE cur_loop;
END IF;
-- cursor loop statements
-- I would like to move this to a map instead of querying for each record
select target_id into db2_empl_id from db2.emp_mapping where source_id = db1_empl_id;
insert into db2.employee(id,name,email) values(db2_empl_id, db1_empl_name, db1_empl_email);
SET cursor_finished = 0; -- hence reset this value for cursor.
end LOOP;
close cur;
END //
DELIMITER ;

Related

Mysql, check if all rows of each column have the same value

A friend of mine wants to check if all the rows, of each of the columns of a table, have the same value.
If they do, then print the value.
Else just print an empty string or null or something.
Imagine this table for example:
+--------+----------+-----+
| Name | Lastname | Age |
+--------+----------+-----+
| Peter | White | 30 |
| Marry | Jane | 30 |
| John | Doe | 30 |
+--------+----------+-----+
The result of the wanted query would output the following:
+--------+----------+-----+
| Name | Lastname | Age |
+--------+----------+-----+
| NULL | NULL | 30 |
+--------+----------+-----+
I tried to create a function where I would get the columns of a given table, loop through each column name and execute a query. But since I am not familiar with Mysql I obviously miss something out and I can't figure out how to achieve what I'm trying to do here.
DROP PROCEDURE IF EXISTS test;
DELIMITER //
create procedure test()
begin
declare i int(11);
declare col_name varchar(50);
declare num_rows int(11);
DECLARE col_names CURSOR FOR
SELECT column_name
FROM information_schema.columns
WHERE table_name='name_of_my_table' and table_schema='name_of_db';
select FOUND_ROWS() as num_rows;
set i = 1;
open col_names;
the_loop: LOOP
IF i > num_rows THEN
CLOSE col_names;
LEAVE the_loop;
END IF;
FETCH col_names
INTO col_name;
-- Here I would like to perform a query for each column
select count(*), col_name from name_of_my_table group by col_name;
-- Then I was thinking of making an if/ else condition to check
-- if I get more than 1 result per column, implying that
-- not all rows have the same value for this column.
SET i = i + 1;
END LOOP the_loop;
CLOSE col_names;
END//
DELIMITER ;
call test;
What this outputs is the count and the column name of the last column found, which does make sense.
I am not sure if what I am trying to do is possible with Mysql only, I can easily do that in PHP but I am wondering if I can do that with a single query as well.
Try the below query.
select if(count(distinct(Name))=1,Name,null), if(count(distinct(Lastname))=1,Lastname,null), if(count(distinct(Age))=1,Age,null)
from your_table;

Case statement in stored procedure - Unknown System Variable

I'm trying to use a case statement to update a column based on the value of another column. My table called 'rma' in MySQL is as follows:
ID | rma_number | serial_number | ref_status_id
1 | 9898 | 123456 | 19
2 | 7869 | 098768 | 3
Here is my stored procedure:
CREATE DEFINER=`admin`#`localhost` PROCEDURE `delete_update_rma`(in selectedID int, in selectedRMAID int)
begin
declare rmaStatus int(5);
select ref_status_id into rmaStatus
from rma
where id = selectedRMAID;
case rmaStatus
when 19 then
set ref_status_id = 0;
end case;
delete from dropbox where id = selectedID;
end
When I try to save to create the procedure I receive the error #1193 - Unknown system variable 'ref_status_id'.
Can anybody help me identify what may be wrong with my stored procedure?
Try this:
CREATE DEFINER=`admin`#`localhost` PROCEDURE `delete_update_rma`(IN selectedID INT, IN selectedRMAID INT)
BEGIN
UPDATE rma
SET ref_status_id =
CASE ref_status_id
WHEN 19 THEN 0
WHEN 3 THEN 2
ELSE ref_status_id
END
WHERE id = selectedRMAID;
DELETE FROM dropbox WHERE id = selectedID;
END
Check the SQL FIDDLE DEMO
OUTPUT
| ID | RMA_NUMBER | SERIAL_NUMBER | REF_STATUS_ID |
|----|------------|---------------|---------------|
| 1 | 12345 | 67898 | 0 |
| 2 | 45645 | 89056 | 2 |
The case syntax is correct, however you cannot change a database field inside a case statement, you need to use an update statement for that.
UPDATE rma
SET ref_status_id = 0
WHERE ref_status_id = 19
AND id = selectedrmaid;
You can only change local vars inside this kind of case statement.
Yours is a typical case of mixing procedural and query logic, a common error when doing stored procedures.
The line ==>
set ref_status_id = 0;
Is causing the error. You are treating set ref_status_id as a variable when its a column in the rma table.
What are you trying to do in that case statement? Since the checking for a hard coded values of 19 doesn't seem to align with what you are trying to do.

how to call a stored procedure using table data?

i want to create a stored procedure by using below requirement.
i tried and written a stored procedure it is working fine with the static values.
how a stored procedure will work with the dynamic values.
Please find my requirement here.
Create a stored proc “skillsparse” that accepts a string of text and and breaks it up into 1,2,3 word phrases.
a. For example: I love java because it’s fun should have these 15 phrases
i. I
ii. I love
iii. I love java
iv. Love
v. Love java
vi. Love java because
vii. Java
viii. Java because
ix. Java because it’s
x. Because
xi. Because it’s
xii. Because it’s fun
xiii. It’s
xiv. It’s fun
xv. fun
3. Store these phrases in a new table called: github_skills_phrases with fields: ID, userid, skills_id (from github_skills_source) and skills_phrase
4. Create a storedproc that compares the skills_phrases against the skills table (ref Table) and store the values into the github_skills table for each user. If possible, please maintain the source of where the skills came from (repodesc, repolang, starred, bio)
5. NOTE: Aside from the info in the new table Kishore is creating, you will also need to run the github_users.bio field against the Skillsparse procedure. You can start this first (for testing logic, etc) since the github_users.bio already exists and has data.
We don’t need to go this for users who have not yet been processed for skills
How i written is:
++++++++++++++++++++++++++++++++++++++++++++++++++++++
DELIMITER $$
CREATE procedure testing(IN id varchar(20),IN usr_id varchar(20),IN str varchar(200))
begin
DECLARE wordCnt varchar(20);
DECLARE wordCnt1 varchar(20);
DECLARE idx INT DEFAULT 1;
DECLARE splt varchar(200);
declare strng varchar(200);
create temporary table tmp.hello1(id varchar(200),usr_id varchar(200),st varchar(200));
set strng = str;
set wordCnt = LENGTH(trim(strng)) - LENGTH(REPLACE(trim(strng), ' ', ''))+1;
set wordCnt1 = LENGTH(trim(strng)) - LENGTH(REPLACE(trim(strng), ' ', ''))+1;
myloop: WHILE idx <= wordCnt DO
set splt = substring_index(trim(strng),' ',idx);
insert into tmp.hello1 values (id,usr_id,splt);
set idx=idx+1;
IF idx = 4 THEN
set strng = substring(trim(strng),length(substring_index(trim(strng),' ',1))+1);
set idx = 1;
set wordCnt = wordCnt -1;
END IF;
end while ;
insert into tmp.hello1 values (id,usr_id,trim(substring(trim(str),length(substring_index(trim(str),' ',wordCnt1-1))+1)));
end $$
Out put ::
mysql> call testing('10','200','I am the my fine the kks hhh nanj kell');
Query OK, 1 row affected (0.77 sec)
mysql> select * from hello1;
+------+--------+---------------+
| id | usr_id | st |
+------+--------+---------------+
| 10 | 200 | I |
| 10 | 200 | I am |
| 10 | 200 | I am the |
| 10 | 200 | am |
| 10 | 200 | am the |
| 10 | 200 | am the my |
| 10 | 200 | the |
| 10 | 200 | the my |
| 10 | 200 | the my fine |
........
..........
| 10 | 200 | kell |
+------+--------+---------------+
27 rows in set (0.00 sec)
my stored procedure is working fine with static values .how to call dynamically a stored procdure by using table data.
Please help me to write a stored procedure to calling with the table data.
If you means you need to call this stored procedure inside select statement on certain data table, this is not available.
You have two options:
1- transfer your procedure to function and then you can call it easily from inside the select statement.
2- write plsql code to call this procedure and you can check the below link about this point
oracle call stored procedure inside select

mysql update trigger: update "some" other rows

Table "Book":
bookid | title
1 | Java
2 | MySQL
3 | Zen
Table "BestPage":
pageid | bookid | isbestpage
1 | 1 | 0
2 | 1 | 0
3 | 1 | 1
4 | 2 | 0
5 | 2 | 1
6 | 2 | 0
So some books may have a "bestpage", but only one.
Now I would probably need a beforeupdate trigger so when I update the row with pageid 2, and set isbestpage = 1,
then all the other pages BUT ONLY FOR THAT BOOK will have isbestpage set to 0.
Something like:
for each row
begin
if old.isbestpage = 1 and -- here I want to tell that **only for the current book** !
then
set new.isbestpage = 0
or something like that.
That is, if I mark a page as bestpage, all other pages within this book should be set as not a best page.
Thanks.
Unfortunately you can't do that in a MySql trigger
According to E.1. Restrictions on Stored Programs
Within a stored function or trigger, it is not permitted to modify a
table that is already being used (for reading or writing) by the
statement that invoked the function or trigger.
What you can do though to alleviate some pain is to create a stored procedure like this
DELIMITER $$
CREATE PROCEDURE sp_update_bestpage(IN pid INT)
BEGIN
UPDATE bestpage p JOIN
bestpage p1 ON p.bookid = p1.bookid
SET p.isbestpage = 0
WHERE p1.pageid = pid AND p.pageid <> pid;
UPDATE bestpage
SET isbestpage = 1
WHERE pageid = pid;
END $$
DELIMITER ;
And then use it
CALL sp_update_bestpage(2);
SQLFiddle

SQL cursor & stored procedure order arrangement

I was working on a stored procedure to update the order field in a product table.
It works only the problem now is the the last item in the loop(cur), is increased twice instead of once (so dubbeled). Like so:
+-----------------+
|product + order |
|_id | |
| | |
| 1 | 0 |
| 2 | 1 |
| etc.. | etc..|
| 36 | 35 |
| 37 | 36 |
| 38 | 38 |
| |
+-----------------+
I cant figure out why. The link table(CategoryProduct) in this case goes to 38 with a category_id of 2 CALL curorder(2);
Stored procedure:
DELIMITER //
CREATE PROCEDURE curorder(
IN catid INT
)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE i INT DEFAULT 0;
DECLARE p INT;
DECLARE cur CURSOR FOR SELECT product_id FROM test.CategoryProduct WHERE category_id = catid;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO p;
UPDATE `test`.`Product` SET `order` = i WHERE `Product`.`product_id` =p;
SET i = i + 1;
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE cur;
END //
DELIMITER ;
The Database is a Mysql Database. Any suggestions for improving the procedure are always welcome.
Thanks in advance.
EDIT:
I already tried to place the SET i STATEMENT beneath the IF STATEMENT but with no result.
You should put:
IF done THEN
LEAVE read_loop;
END IF;
Above your update statement, the last time mysql walks trough the loop is uses the old variables because there is no new 'p'. but i is incremented.
I good way to debug stored procedures is with a log table:
CREATE TABLE procedureLog
(
id INTEGER AUTO_INCREMENT,
description TEXT,
PRIMARY KEY (id)
);
For this case you can log the update parameters with the follow query:
INSERT INTO  `test`.`procedureLog` (`id` ,`description`) VALUES (null,  CONCAT('id: ', CAST(p as CHAR), ' order: ', CAST(i as CHAR)));
Good luck!