I noticed a very interesting (and unexpected as well) thing yesterday. I was given a task (on production environment) to update three columns of TableA (I am changing the table and column names due to some obvious reasons) by getting all the values present in dummytable. The primary key of both the tables is column A. I know that this task was very simple and could be accomplished in several ways but I chose to write a stored procedure (given below) for that.
When the stored procedure was finished executing then it was noticed that columns B, C & statusCode were having the same values (i.e. thousands of records were having identical values in these three columns). Can someone tell me what went wrong?
1) What's wrong (or missing) in this stored procedure? (Dummy table had thousands of records as well)
2) What could be the best possible way of doing this task other than creating a stored procedure?
PS: I created (executed as well) this stored procedure on production environment using MySQL workbench and I got an exception during the execution of the procedure which stated something "Lost connection to MySQL server" but I guess since I was running this procedure on the remote machine then there was no interruption on the server while the procedure was executing.
Here is my stored procedure.
DELIMITER $$
CREATE DEFINER=`ABC`#`%` PROCEDURE `RetrieveExtractionData`()
BEGIN
DECLARE claimlisttraversed BOOLEAN DEFAULT FALSE;
DECLARE a VARCHAR(20);
DECLARE b INTEGER;
DECLARE c INTEGER;
DECLARE claimlist CURSOR FOR SELECT
`dummytable`.`A`,
`dummytable`.`B`,
`dummytable`.`C`
FROM `ABC`.`dummytable`;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET claimlisttraversed = TRUE;
OPEN claimlist;
claimlistloop: LOOP
FETCH claimlist INTO a, b, c;
IF claimlisttraversed THEN
CLOSE claimlist;
LEAVE claimlistloop;
END IF;
UPDATE `ABC`.`TableA`
SET
`B` = b,
`C` = c,
`statuscode` = 'Sent'
WHERE `A` = a;
END LOOP claimlistloop;
END
For your first question:
1) What's wrong (or missing) in this stored procedure? (Dummy table had
thousands of records as well)
I guess you forgot to CLOSE the CURSOR. Right after you end the LOOP, you should CLOSE the CURSOR.
END LOOP claimlistloop;
CLOSE claimlist;
END
2) What could be the best possible way of doing this task other than
creating a stored procedure?
Doing that in the STORED PROCEDURE should be fine. And also using CURSOR would be fine since you will just execute the procedure once (I guess because this is a production fix).
But from your question, you just want to update TableA based from the provided DummyTable. I assume that these tables have the same columns.
So I think this query is better than the CURSOR:
UPDATE TableA A
INNER JOIN DummyTable D ON D.A = A.A
SET A.B = D.B
, A.C = D.C
, A.statuscode = 'Sent';
But please try it first on a backup or dummy table. I haven't tested it yet.
Forget the cursor. In fact you should never use a cursor if it's avoidable. Cursors are incredibly slow.
Simply do
UPDATE
yourTable yt
INNER JOIN dummyTable dt ON yt.A = dt.A
SET
yt.B = dt.B,
yt.C = dt.C;
and you're fine.
1) What's wrong (or missing) in this stored procedure? (Dummy table
had thousands of records as well)
2) What could be the best possible
way of doing this task other than creating a stored procedure?
IMHO the most important thing that you're currently missing is that you don't need any cursors for that. Your whole stored procedure is one UPDATE statement. Execute it alone or wrap it in a stored procedure
CREATE PROCEDURE RetrieveExtractionData()
UPDATE TableA a JOIN dummytable d
ON a.a = d.a
SET a.b = d.b, a.c = d.c, a.statuscode = 'Sent';
You don't even need to change a delimiter and use BEGIN ... END block
Here is SQLFiddle demo.
Related
I'm trying to create a scorecard application and I have 3 separate MySQL tables that I want to INSIDE JOIN based on the Round_ID (keeping all rows with the same Round_ID together).
I've figured out how to join based on this, but now my idea is to use a WHILE loop to iterate through each Round_ID entered in the database and create a separate JOIN table for each one (1,2,3 etc) so I can have a separate scorecard table for each round entered. I decided to put the statements in a procedure so I can call it later on easier. So far this is what I have -
DELIMITER $$
CREATE PROCEDURE get_scorecard()
BEGIN
DECLARE increment INT;
DECLARE maxRound INT;
SET increment = 1;
SET maxRound = (SELECT MAX(Round_ID) FROM Course_Played);
WHILE increment <= maxRound
BEGIN
SELECT Player_Scores.Round_ID, Players.Player_ID, Players.Player_Name, Player_Scores.Player_Score, Course_Played.Course_ID, Course_Played.Course_Name
FROM Players
INNER JOIN Player_Scores
ON Players.Player_ID = Player_Scores.Player_FK
JOIN Course_Played
ON Course_Played.Course_ID = Player_Scores.Course_FK
WHERE Round_ID = increment
SET #increment = increment + 1;
END
END$$
DELIMITER ;
But the code does not work. I keep getting a "check syntax error" and can't figure out where it went wrong. (As a side note, I am new to SQL and new to programming in general). I am also using phpMyAdmin if it matters.
Also, using a loop for this really the best way to accomplish this? I looked at cursors but don't know if that would be applicable in this situation...
Any help is greatly appreciated!!
I want to write a MySQL procedure. It should take a parameter, which is table name.
I have two tables with similar schema and I want to perform similar operations on them. I should run a select query which is a self join and then load results into cursor and do something based on that.
CREATE PROCEDURE MY_PROCEDURE (table_name CHAR(15)
BEGIN
//
DECLARE A CURSOR HERE, WRITE AN INNER JOIN QUERY AND LOAD results into cursor
//
.......
.......
.......
END
Now I need to call the Procedure
CALL MY_PROCEDURE('First_table');
CALL MY_PROCEDURE('Second_table');
My goal is to re-use code here.
Background - I have a DB created from a single large flat file. Instead of creating a single large table with 106 columns. I created a "columns" table which stores the column names and the id of the table that holds that data, plus 106 other tables to store the data for each column. Since not all the records have data in all columns, I thought this might be a more efficient way to load the data (maybe a bad idea).
The difficulty with this was rebuilding a single record from this structure. To facilitate this I created the following procedure:
DROP PROCEDURE IF EXISTS `col_val`;
delimiter $$
CREATE PROCEDURE `col_val`(IN id INT)
BEGIN
DROP TEMPORARY TABLE IF EXISTS tmp_record;
CREATE TEMPORARY TABLE tmp_record (id INT(11), val varchar(100)) ENGINE=MEMORY;
SET #ctr = 1;
SET #valsql = '';
WHILE (#ctr < 107) DO
SET #valsql = CONCAT('INSERT INTO tmp_record SELECT ',#ctr,', value FROM col',#ctr,' WHERE recordID = ',#id,';');
PREPARE s1 FROM #valsql;
EXECUTE s1;
DEALLOCATE PREPARE s1;
SET #ctr = #ctr+1;
END WHILE;
END$$
DELIMITER ;
Then I use the following SQL where the stored procedure parameter is the id of the record I want.
CALL col_val(10);
SELECT c.`name`, t.`val`
FROM `columns` c INNER JOIN tmp_record t ON c.ID = t.id
Problem - The first time I run this it works great. However, each subsequent run returns the exact same record even though the parameter is changed. How does this persist even when the stored procedure should be dropping and re-creating the temp table?
I might be re-thinking the whole design and going back to a single table, but the problem illustrates something I would like to understand.
Unsure if it matters but I'm running MySQL 5.6 (64 bit) on Windows 7 and executing the SQL via MySQL Workbench v5.2.47 CE.
Thanks,
In MySQL stored procedures, don't put an # symbol in front of local variables (input parameters or locally declared variables). The #id you used refers to a user variable, which is kind of like a global variable for the session you're invoking the procedure from.
In other words, #id is a different variable from id.
That's the explanation of the immediate problem you're having. However, I would not design the tables as you have done.
Since not all the records have data in all columns, I thought this might be a more efficient way to load the data
I recommend using a conventional single table, and use NULL to signify missing data.
So far I have tried many different ways of accessing the data on three tables using a stored procedure. First I tried a simple select statement :
create procedure reportCodes () begin
SELECT Numbers.serial_numb, numOwner.lName, numOwner.fName, numOwner.email,
location.long, location.lat, Numbers.dateValidated
FROM Numbers, Owner, location
WHERE Numbers.used = true AND Numbers.id=numOwner.Numbers_id AND
Numbers.id=location.Numbers_id;
end$$
(names changed to protect the guilty)
Running the stored procedure in phpmyadmin results in the first instance of the record (1 out of two ‘true’ in the test database). Running just:
SELECT Numbers.serial_numb, numOwner.lName, numOwner.fName, numOwner.email,
location.long, location.lat, Numbers.dateValidated
FROM Numbers, Owner, location
WHERE Numbers.used = true AND Numbers.id=numOwner.Numbers_id AND
Numbers.id=location.Numbers_id;
in the phpmyadmin SQL tab returns both records. Then I tried a temp table:
create procedure reportCodes () begin
CREATE TEMPORARY TABLE used_numbers AS (
SELECT Numbers.serial_numb, numOwner.lName, numOwner.fName, numOwner.email,
location.long, location.lat, Numbers.dateValidated
FROM Numbers, Owner, location
WHERE Numbers.used = true AND Numbers.id=numOwner.Numbers_id AND
Numbers.id=location.Numbers_id);
SELECT * FROM used_numbers; end$$
Returns 1 of 2 records as the procedure but both records in console. Finally I tried changing my table to a join:
CREATE PROCEDURE reportCodes()
begin
create temporary table used_numbers AS (
SELECT Numbers.serial_numb, numOwner.lName, numOwner.fName, numOwner.email,
location.long, location.lat, Numbers.dateValidated
FROM Numbers JOIN numOwner
ON Numbers.id=numOwner.Numbers_id
JOIN location ON
numOwner.Numbers_id=location.Numbers_id
WHERE Numbers.used = true
);
SELECT * FROM used_numbers; end$$
Same results as above. I’m at a loss as to why running just the SQL would show both test records but running the procedure with the exact same code only yields one.
Thanks
in your query, numOwners isn't a valid table being selected against, so something's wrong. Have you tried running your SQL in the Query window in phpMyAdmin to ensure that the EXACT same query is returning 2 rows?
I presume the "Owner" table is supposed to be "numOwner", so I've re-written the stored procedure call below. Also, I'm not sure what types of values you're storing in Numbers.used to evaluate to "TRUE". I will presume you're using a TINYINT(1), so I've altered that, as well. I hope this helps.
DELIMITER $$
USE `db`$$
DROP PROCEDURE IF EXISTS `reportCodes`$$
CREATE PROCEDURE `reportCodes`()
BEGIN
SELECT
n.serial_numb,
o.lName,
o.fName,
o.email,
l.long,
l.lat,
n.dateValidated
FROM Numbers n
INNER JOIN numOwner o ON n.id=o.Numbers_id
INNER JOIN location l ON n.id=l.Numbers_id;
WHERE n.used = 1
END$$
DELIMITER ;
Ok, First off, I am not a mysql guru. Second, I did search, but saw nothing relevant related to mysql, and since my DB knowledge is limited, guessing syntactical differences between two different Database types just isn't in the cards.
I am trying to determine if a particular value already exists in a table before inserting a row. I've decided to go about this using two Stored procedures. The first:
CREATE PROCEDURE `nExists` ( n VARCHAR(255) ) BEGIN
SELECT COUNT(*) FROM (SELECT * FROM Users WHERE username=n) as T;
END
And The Second:
CREATE PROCEDURE `createUser` ( n VARCHAR(255) ) BEGIN
IF (nExists(n) = 0) THEN
INSERT INTO Users...
END IF;
END
So, as you can see, I'm attempting to call nExists from createUser. I get the error that no Function exists with the name nExists...because it's a stored procedure. I'm not clear on what the difference is, or why such a difference would be necessary, but I'm a Java dev, so maybe I'm missing some grand DB-related concept here.
Could you guys help me out by any chance?
Thanks
I'm not sure how it helped you, but...
why SELECT COUNT(*) FROM (SELECT * FROM Users WHERE username=n) and not just SELECT COUNT(*) FROM Users WHERE username=n?
Just make the user name (or whatever the primary application index is) a UNIQUE index and then there is no need to test: Just try to insert a new record. If it already exists, handle the error. If it succeeds, all is well.
It can (and should) all be one stored procedure.