MySQL procedure temporary table Select - mysql

I'm trying to create a temporary table and select results from it, inside a mysql procedure.
Here's a piece of the SQL
DELIMITER $$
CREATE PROCEDURE `mydb`.`simulate_results` (currdate DATETIME, enddate DATETIME, idcam INT)
BEGIN
DECLARE MYVAR1,
MYVAR2,
MYVAR3,
IDX INT;
CREATE TEMPORARY TABLE IF NOT EXISTS tmp (
`Id` INT NOT NULL AUTO_INCREMENT,
`Field1` INT NOT NULL,
`Field2` INT NOT NULL,
`Field3` INT NOT NULL, PRIMARY KEY (`Id`)) ENGINE=MEMORY;
INSERT INTO tmp (`Field1`,`Field2`,`Field3`) VALUES (0,0,0);
INSERT INTO tmp (`Field1`,`Field2`,`Field3`) VALUES (1,0,0);
SELECT Field1,Field2,Field3,Id INTO MYVAR1,MYVAR2,MYVAR3,IDXFROM tmp ORDER BY RAND() LIMIT 1;
SELECT MYVAR1; (...)
The only variable that comes filled from the SELECT INTO statemente is IDX (primary key). The others are always NULL.
Is there something wrong with this code?

Have you looked at your temporary table to see what kind of data is lingering in it? The temporary table will still be there after the procedure completes and if you run the procedure again from the same session, you'll be writing new rows into the existing temporary table.
I would add DROP TEMPORARY TABLE IF EXISTS tmp before CREATE TEMPORARY TABLE ... to be sure you don't have old data hanging around.

Related

Create a temporary table inside procedure and delete from event

I have a procedure like this :
create
procedure new_generation(IN id bigint unsigned)
BEGIN
create temporary table if not exists tmp_mine
(
id bigint unsigned primary key not null auto_increment,
created_at timestamp,
);
end
now I want to create an event for delete old rows.
create event per_ten_second on schedule
every '10' SECOND
starts '2021-10-26 11:49:16'
enable
do
delete from tmp_mine where created_at < now();
but it doesn't work. Can anybody give me a clue? Can we use generated temp table inside stored procedure out of it? for example inside an event.

Performing SELECT but INSERT when NOT EXISTS in MySQL

I have a MySQL table created using the following syntax:
CREATE TABLE `name_to_id` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(128),
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
);
And a common query this table would like to answer is name to id look-up, but if the <name, id> pair does not exist in the DB, then also insert a new entry and return the newly inserted id.
Can I know should I do that in MySQL?
As commented by Strawberry, this cannot be performed in a single query.
However, here is a stored procedure that should do what you expect. First, it uses the INSERT ... ON DUPLICATE KEYS UPDATE syntax to insert new names ; this actually relies on the UNIQUE key that you correctly set up on the name column.
DELIMITER //
CREATE PROCEDURE get_id_by_name(IN p_name VARCHAR(128))
BEGIN
INSERT INTO name_to_id(name) VALUE(p_name) ON DUPLICATE KEY UPDATE name = p_name;
SELECT id FROM name_to_id WHERE name = p_name;
END //
DELIMITER ;
Demo on DB Fiddle.
This approach is efficient, but the downside of ON DUPLICATE KEYS is that it wastes id sequences : everytime the query is called, the sequence is autoincremented (even if a record already exists). This can be seen in the fiddle.
Here is another approach, that won't burn sequence numbers :
DELIMITER //
CREATE PROCEDURE get_id_by_name(IN p_name VARCHAR(128))
BEGIN
DECLARE p_id bigint(20) unsigned;
SELECT id INTO p_id FROM name_to_id WHERE name = p_name;
IF (p_id IS NULL) THEN
INSERT INTO name_to_id(name) VALUE(p_name);
SELECT LAST_INSERT_ID();
ELSE
SELECT p_id;
END IF;
END //
DELIMITER ;
Demo on DB Fiddle.
you can do this on stored proc, if the select statement did not return a result, then you can execute the insert statement

Idempotent table and index creation with value insertion in MySQL

I have some SQL Server schema changes that I'm trying to convert to MySQL. I know about CREATE TABLE IF NOT EXISTS in MySQL. I don't think I can use that here.
What I want to do is create a table in MySQL, with an index, and then insert some values all as part of the "if not exists" predicate. This was what I came up with, though it doesn't seem to be working:
SET #actionRowCount = 0;
SELECT COUNT(*) INTO #actionRowCount
FROM information_schema.tables
WHERE table_name = 'Action'
LIMIT 1;
IF #actionRowCount = 0 THEN
CREATE TABLE Action
(
ActionNbr INT AUTO_INCREMENT,
Description NVARCHAR(256) NOT NULL,
CONSTRAINT PK_Action PRIMARY KEY(ActionNbr)
);
CREATE INDEX IX_Action_Description
ON Action(Description);
INSERT INTO Action
(Description)
VALUES
('Activate'),
('Deactivate'),
('Specified');
END IF
I can run it once, and it'll create the table, index, and values. If I run it a second time, I get an error: Table Action already exists. I would have thought that it wouldn't run at all if the table already exists.
I use this pattern a lot when bootstrapping a schema. How can I do this in MySQL?
In mysql compound statements can only be used within stored programs, which includes the if statement as well.
Therefore, one solution is to include your code within a stored procedure.
The other solution is to use the create table if not exists ... with the separate index creation included within the table definition and using insert ignore or insert ... select ... to avoidd inserting duplicate values.
Examples of options:
Option 1:
CREATE TABLE IF NOT EXISTS `Action` (
`ActionNbr` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`Description` VARCHAR(255) NOT NULL,
INDEX `IX_Action_Description` (`Description`)
) SELECT 'Activate' `Description`
UNION
SELECT 'Deactivate'
UNION
SELECT 'Specified';
Option 2:
DROP PROCEDURE IF EXISTS `sp_create_table_Action`;
DELIMITER //
CREATE PROCEDURE `sp_create_table_Action`()
BEGIN
IF NOT EXISTS(SELECT NULL
FROM `information_schema`.`TABLES` `ist`
WHERE `ist`.`table_schema` = DATABASE() AND
`ist`.`table_name` = 'Action') THEN
CREATE TABLE `Action` (
`ActionNbr` INT AUTO_INCREMENT,
`Description` NVARCHAR(255) NOT NULL,
CONSTRAINT `PK_Action` PRIMARY KEY (`ActionNbr`)
);
CREATE INDEX `IX_Action_Description`
ON `Action` (`Description`);
INSERT INTO `Action`
(`Description`)
VALUES
('Activate'),
('Deactivate'),
('Specified');
END IF;
END//
DELIMITER ;
CALL `sp_create_table_Action`;

Iterating without using cursor in MYSQL

My task is to write a stored procedure that will first validate the data from a temporary table and then insert the data into the main table.
For this I am planning to iterate over each row of the temp table, validate it using some other stored procedure or user defined function and then insert the data into the main table.
My problem is how to iterate over the rows of temp table without using CURSORS because they are very slow and memory consuming. I want to use some looping structure instead of CURSOR.
Of course if any one has any other algorithm for the above problem there suggestions are welcome.
PS: I am using MYSQL DB
Without the use of Cursor, you could iterate using a temporary table and a While..Do statement.
Let's say you have two tables
CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
And
CREATE TABLE `tmp_user` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
Create the following routine, and adjust the validation process:
DELIMITER $$
USE `routines_sample`$$
CREATE PROCEDURE `nocursor`()
BEGIN
Declare validId int;
Declare validName varchar(45);
-- drop the temporary table if exists
Drop table if exists `routines_sample`.tmp_validation;
-- create the temporary table, here you could use Engine=Memory
Create table `routines_sample`.tmp_validation (`id` int not null, `name` varchar(45) not null, `valid` bit(1) not null) Engine=MyISAM;
-- insert into the temporary table with a valid flag = 0 (false)
Insert into `routines_sample`.tmp_validation (`id`, `name`, `valid`)
Select tu.id, tu.name, 0
From `routines_sample`.tmp_user tu;
-- while exists data to validate on temporary table do something
While exists(Select `id` From `tmp_validation` Where `valid` = 0) Do
Select `id`, `name` Into #validId, #validName From tmp_validation Where `valid` = 0 Limit 1;
-- do your validation
Select #validId, #validName;
-- don't forget to update your validation table, otherwise you get an endless loop
Update `tmp_validation`
Set `valid` = 1
Where `id` = #validId;
END WHILE;
-- insert only valid rows to your destination table
Insert into `routines_sample`.`user` (`name`)
Select `name` From `tmp_validation`
Where `valid` = 1;
-- drop the temporary table
DROP TABLE tmp_validation;
END$$
DELIMITER ;

MySQL procedure to load data from staging table to other tables. Need to split up multivalue field in the process

I'm trying to export data from a multivalue database (Unidata) into MySQL. Lets say my source data was a person's ID number, their first name and all the states they've lived in. The states field is a multi value field and I'm exporting them so that the different values within that field are seperated by a ~. A sample extract looks like:
"1234","Sally","NY~NJ~CT"
"1235","Dave","ME~MA~FL"
"3245","Fred","UT~CA"
"2344","Sue","OR"
I've loaded this data into a staging table
Table:staging
Column 1: personId
Column 2: name
Column 3: states
What I want to do is split this data out into two tables using a procedure: a persons table and a states table. A person can have many entries in the states table:
Table 1: persons
Column 1: id
Column 2: name
Table 2: states
Column 1: personId
Column 2: state
My procedure takes the data from the staging table and dumps it over to table 1 just fine. However, i'm a little lost how how to split the data up and send it to table 2. Sally would need to have three entries in the states table (NY, NJ, CT), Dave would have 3, Fred would have 2 and Sue would have1 (OR). Any ideas on how to accomplish this?
try something like this : http://pastie.org/1213943
-- TABLES
drop table if exists staging;
create table staging
(
person_id int unsigned not null primary key,
name varchar(255) not null,
states_csv varchar(1024)
)
engine=innodb;
drop table if exists persons;
create table persons
(
person_id int unsigned not null primary key,
name varchar(255) not null
)
engine=innodb;
drop table if exists states;
create table states
(
state_id tinyint unsigned not null auto_increment primary key, -- i want a nice new integer based PK
state_code varchar(3) not null unique, -- original state code from staging
name varchar(255) null
)
engine=innodb;
/*
you might want to make the person_states primary key (person_id, state_id) depending on
your queries as this is currently optimised for queries like - select all the people from NY
*/
drop table if exists person_states;
create table person_states
(
state_id tinyint unsigned not null,
person_id int unsigned not null,
primary key(state_id, person_id),
key (person_id)
)
engine=innodb;
-- STORED PROCEDURES
drop procedure if exists load_staging_data;
delimiter #
create procedure load_staging_data()
proc_main:begin
truncate table staging;
-- assume this is done by load data infile...
set autocommit = 0;
insert into staging values
(1234,'Sally','NY~NJ~CT'),
(1235,'Dave','ME~MA~FL'),
(3245,'Fred','UT~CA'),
(2344,'Sue','OR'),
(5555,'f00','OR~NY');
commit;
end proc_main #
delimiter ;
drop procedure if exists cleanse_map_staging_data;
delimiter #
create procedure cleanse_map_staging_data()
proc_main:begin
declare v_cursor_done tinyint unsigned default 0;
-- watch out for variable names that have the same names as fields !!
declare v_person_id int unsigned;
declare v_states_csv varchar(1024);
declare v_state_code varchar(3);
declare v_state_id tinyint unsigned;
declare v_states_done tinyint unsigned;
declare v_states_idx int unsigned;
declare v_staging_cur cursor for select person_id, states_csv from staging order by person_id;
declare continue handler for not found set v_cursor_done = 1;
-- do the person data
set autocommit = 0;
insert ignore into persons (person_id, name)
select person_id, name from staging order by person_id;
commit;
-- ok now we have to use the cursor !!
set autocommit = 0;
open v_staging_cur;
repeat
fetch v_staging_cur into v_person_id, v_states_csv;
-- clean up the data (for example)
set v_states_csv = upper(trim(v_states_csv));
-- split the out the v_states_csv and insert
set v_states_done = 0;
set v_states_idx = 1;
while not v_states_done do
set v_state_code = substring(v_states_csv, v_states_idx,
if(locate('~', v_states_csv, v_states_idx) > 0,
locate('~', v_states_csv, v_states_idx) - v_states_idx,
length(v_states_csv)));
set v_state_code = trim(v_state_code);
if length(v_state_code) > 0 then
set v_states_idx = v_states_idx + length(v_state_code) + 1;
-- add the state if it doesnt already exist
insert ignore into states (state_code) values (v_state_code);
select state_id into v_state_id from states where state_code = v_state_code;
-- add the person state
insert ignore into person_states (state_id, person_id) values (v_state_id, v_person_id);
else
set v_states_done = 1;
end if;
end while;
until v_cursor_done end repeat;
close v_staging_cur;
commit;
end proc_main #
delimiter ;
-- TESTING
call load_staging_data();
select * from staging;
call cleanse_map_staging_data();
select * from states order by state_id;
select * from persons order by person_id;
select * from person_states order by state_id, person_id;