I have a simple insert select which insert _TABLE_B_ data in _TABLE_A_ new row
INSERT INTO _TABLE_A_(_USERNAME_,_ID_)
SELECT _USERNAME_,_ID_
FROM _TABLE_B_
I want to insert a row in a table named _TABLE_C_ each time i insert a row in _TABLE_A_ and add the current inserted _TABLE_C_ id in _TABLE_A_.
i'll try to explain it in an other way :
INSERT INTO _TABLE_A_(_USERNAME_,_ID_,_FOREIGN_ID_)
SELECT B._USERNAME_,B._ID_,C._FOREIGN_ID_
FROM _TABLE_B_ AS B
LEFT JOIN _TABLE_C_ AS C
#Insert a row in _TABLE_C_ to retrieve _FOREIGN_ID_...
I'm searching for a single minimal query which have the INSERT SELECT statement like mine because insert select can loop and i have to loop.
FYI :
I'm in a stored procedure.
I also use prepared statements with dynamic data, and cursors is not suitable for dynamic data select...
I would do all the INSERTs in _TABLE_C_ first and then join it in the INSERT _TABLE_A_ to get the appropriate foreign keys.
If that is not possible, I would use a cursor.
Cursor on _TABLE_B_ & Fetch
INSERT _TABLE_C_
INSERT _TABLE_A_ with Foreign_Id = SCOPE_IDENTITY()
Fetch next
I found a solution.
create a temporary table and add dynamic select statement which retrieve the primary keys (id)
declare a cursor and select this temporary table id ( variables doesn't work but temporary tables do )
execute statement to create temporary table
open the cursor and iterate the inserts
EXAMPLE
BEGIN
DECLARE isDone INT DEFAULT 0;
DECLARE fetchedmemberWhoWillReceiveMailId int;
DECLARE cur1 CURSOR FOR SELECT id FROM memberWhoWillReceiveMail;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET isDone = 1;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET #sexe = VAR_sexe;
SET #event = VAR_eventId;
SET #subject = VAR_subject;
SET #body = VAR_body;
SET #to = VAR_to;
SET #from = VAR_from;
SET #region = VAR_region;
SET #departement = VAR_departement;
SET #age = VAR_age;
SET #baseSqlStatement =' CREATE TEMPORARY TABLE memberWhoWillReceiveMail SELECT e.id FROM TABLE_A as e LEFT JOIN TABLE_B AS a on a.member_id = e.id';
SET #whereSqlStatement= 'WHERE e.is_visible = 1 AND e.member_group_id IN (10,11) ';
IF (#region!='') THEN
SET #whereSqlStatement= CONCAT(#whereSqlStatement,' AND region=',#region);
END IF;
IF (#event !=null ) THEN
SET #whereSqlStatement= CONCAT(#whereSqlStatement,' AND m.event_id !=' ,#eventId);
END IF;
IF (#sexe!=null ) THEN
SET #whereSqlStatement= CONCAT(#whereSqlStatement,' AND e.sexe=',#sexe);
END IF;
SET #baseSqlStatement = CONCAT(#baseSqlStatement,#whereSqlStatement);
START TRANSACTION;
PREPARE stmt1 FROM #baseSqlStatement;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
OPEN cur1;
FETCH cur1 INTO fetchedmemberWhoWillReceiveMailId;
WHILE NOT isDone DO
INSERT INTO conversation(created_at,updated_at)VALUES(now(),now());
INSERT INTO message(created_at,updated_at,from, to, uniqueID) VALUES(now(),now(),#from,fetchedmemberWhoWillReceiveMailId,LAST_INSERT_ID() );
FETCH cur1 INTO fetchedmemberWhoWillReceiveMailId; END WHILE; CLOSE cur1;
COMMIT;
DROP TEMPORARY TABLE IF EXISTS memberWhoWillReceiveMail;
END
Related
I'm new to Mysql Stored Procedures.
Tring to return rows in a stored procedure after a LOOP.
Here's my code
BEGIN
DECLARE date_SD date;
DECLARE c_stack CURSOR FOR
select SD from t4 where date(SD) >= "2022-05-01" and date(SD)<= "2022-05-30" group by SD;
DROP TEMPORARY TABLE IF EXISTS final_result;
CREATE TEMPORARY TABLE final_result LIKE templaedb.temp_table;
OPEN c_stack;
read_loop: LOOP
FETCH c_stack INTO date_SD;
INSERT INTO final_result VALUES ('first','140','2022-05-06','','1','2','3','4','5');
INSERT INTO final_result VALUES ('last','500','2022-05-06','','11','12','13','14','15');
END LOOP read_loop;
CLOSE c_stack;
select 'Print Test';
select * from final_result;
END
Select statement at last of the Stored Procedure is not working.
Try this
DECLARE date_SD date;
DECLARE c_stack CURSOR FOR
select SD from t4 where date(SD) >= "2022-05-01" and date(SD)<= "2022-05-30" group by SD;
/* add this*/ DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
DROP TEMPORARY TABLE IF EXISTS final_result;
CREATE TEMPORARY TABLE final_result LIKE templaedb.temp_table;
OPEN c_stack;
read_loop: LOOP
FETCH c_stack INTO date_SD;
/* must include*/
IF done = 1 THEN
LEAVE read_loop;
END IF;
/* must include*/
INSERT INTO final_result VALUES ('first','140','2022-05-06','','1','2','3','4','5');
INSERT INTO final_result VALUES ('last','500','2022-05-06','','11','12','13','14','15');
END LOOP read_loop;
CLOSE c_stack;
select * from final_result;
I have within a database several tables where they all have username column. I would like to update one username and naturally I should update it in all tables.
I have this working solution:
UPDATE `user`,
`user_images`,
`user_comments`
SET `user`.`username` = 'new_name',
`user_images`.`username` = 'new_name',
`user_comments`.`username` = 'new_name'
WHERE `user`.`username` = 'old_name'
AND `user_images`.`username` = 'old_name'
AND `user_comments`.`username` = 'old_name'
I am hoping for a better query that can do the same action, as if table numbers got increased, do I really need to do this in 100 lines?
It sounds painful if you have to update each table. I would suggest using a stored procedure to finish the tedious job. Fisrt of all , make a table(named tablelist) which list all the tablename you would like to update. Then call the procedure by providing the two parameters where the o_name is the name you would like to change and the n_name is the new name to be changed into.
delimiter //
drop procedure if exists update_name//
create procedure update_name (o_name varchar(30),n_name varchar(30))
begin
declare t_name varchar(30);
declare done bool default false;
declare csr cursor for select tablename from tablelist;
declare continue handler for not found set done=true;
open csr;
lp: loop
fetch csr into t_name;
if done=true then
leave lp;
end if;
set #prep=concat('update ',t_name,' set `username`= "',n_name,'" where `username`= "',o_name,'";');
prepare prep_stat from #prep;
execute prep_stat;
deallocate prepare prep_stat;
end loop lp;
close csr;
end//
delimiter ;
The following call will change the name from john(case insensitive) to Xero in all tables listed in the tablelist table.
call update_name('John','Xero');
My query should while looping through cursor for table table_a check if a value exists in table_b. If a value exists in table_b then return a value into the variable #yyy.
When I run this stored proc I should get a value that returns col2,col3,col1. but it returns only col2, col3.
In this query when I'm using the into #yyy I feel its not working the way it needs to. Not sure what the problem is. Can you please help.
Just by removing into #yyy I can kind of get right results but I needs to make more changes to the variable #yyy which is why I need to store the results into it.
Delimiter $$
DROP PROCEDURE IF EXISTS sp_test3;
CREATE PROCEDURE sp_test3()
BEGIN
DECLARE DONE INT DEFAULT 0;
DECLARE col1 varchar(255);
DECLARE curA CURSOR FOR SELECT a1 FROM table_a;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET DONE = 1;
OPEN curA;
SET #SQL_TXT = '';
while done = 0 do
fetch next from CurA into col1;
if done = 0 then
SET #xxx = CONCAT("select b1 into #yyy from table_b where b1 ='",
col1,"'");
PREPARE stmt_name FROM #xxx;
EXECUTE stmt_name;
DEALLOCATE PREPARE stmt_name;
SELECT #yyy;
END IF;
END WHILE;
close curA;
end
$$
create table scripts below:
create table table_a(a1 varchar(255));
create table table_b(b1 varchar(255));
insert into table_a values('col2');
insert into table_a values('col3');
insert into table_a values('col5');
insert into table_a values('col1');
insert into table_b values('col2');
insert into table_b values('col3');
insert into table_b values('col4');
insert into table_b values('col1');
drop procedure if exists sp_test3;
drop table if exists table_b, table_a;
create table if not exists table_a(a1 varchar(255));
create table if not exists table_b(b1 varchar(255));
insert into table_a values ('col2');
insert into table_a values ('col3');
insert into table_a values ('col5');
insert into table_a values ('col1');
insert into table_b values ('col2');
insert into table_b values ('col3');
insert into table_b values ('col4');
insert into table_b values ('col1');
CREATE PROCEDURE sp_test3()
BEGIN
DECLARE DONE, DONE1 INT DEFAULT 0;
DECLARE col1 varchar(255);
DECLARE curA CURSOR FOR SELECT a1 FROM table_a;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET DONE = 1;
OPEN curA;
SET #SQL_TXT = '';
while done = 0 do
fetch next from CurA into col1;
if done = 0 then
BEGIN
DECLARE CONTINUE HANDLER FOR NOT FOUND SET DONE1 = 1;
SET #xxx = CONCAT("select b1 into #yyy
from table_b
where b1 = '", col1, "'");
PREPARE stmt_name FROM #xxx;
EXECUTE stmt_name;
DEALLOCATE PREPARE stmt_name;
if (DONE1 = 0) THEN
SELECT #yyy;
ELSE
SET DONE1 = 0;
END IF;
END;
END IF;
END WHILE;
close curA;
end;
I have a table containing 100,000+ records from which i have some calculations to do based on a value which is stored in a specific column.
For this i have written a stored procedure which takes 100 rows at a time from the base table. Since cursors don't support dynamic sql. What i did was i create a dynamic view and the cursor always selects data from that view.
The code :
BEGIN
-- VARIABLES TO RETURN
DECLARE ORIG_COUNT INT ;
DECLARE SERVED_COUNT INT;
-- VARIABLED FOR LOOPING
DECLARE no_more_rows BOOLEAN;
DECLARE no_more_rows_sub BOOLEAN;
DECLARE num_rows INT DEFAULT 0;
SET #ORIG_COUNT =0;
SET #SERVED_COUNT=0;
SELECT COUNT(*) FROM table_one AS A WHERE A.year='2011' INTO #ORIG_COUNT ;
DELETE FROM table_test;
insert into table_test values ('Total Rows',#ORIG_COUNT,NULL);
DROP TABLE IF EXISTS table_request;
CREATE TABLE table_request (
BN_NUM VARCHAR(25) NOT NULL,
DN_NUM varchar(10) NOT NULL,
A_TOTAL FLOAT DEFAULT 0,
B_TOTAL FLOAT DEFAULT 0,
C_TOTAL FLOAT DEFAULT 0,
PRIMARY KEY (BN)
);
WHILE (#SERVED_COUNT<#ORIG_COUNT) DO
DROP VIEW IF EXISTS pbs_history.temp_view;
SET #query = CONCAT('CREATE VIEW temp_view as SELECT A.BN,A.DN
FROM table_one AS A
WHERE A.year='2011'
ORDER BY A.ID
LIMIT ', #SERVED_COUNT,',100');
PREPARE stmt from #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
/* ================= HERE ==================== */
SELECT * FROM temp_view;
BLOKA:BEGIN
DECLARE BN VARCHAR(25);
DECLARE DN VARCHAR(10);
DECLARE num_rows INT(5) DEFAULT 0;
DECLARE bn_count INT(5) DEFAULT 0;
DECLARE sel_recs CURSOR FOR SELECT * FROM temp_view;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = TRUE;
insert into table_test values('Start Served',#SERVED_COUNT,#query);
OPEN sel_recs;
SELECT FOUND_ROWS() INTO #num_rows;
insert into table_test values('Found Rows in View',#num_rows,NULL);
the_loop: LOOP
FETCH sel_recs INTO BN,DN;
IF no_more_rows THEN
CLOSE sel_recs;
LEAVE the_loop;
END IF;
SET #bn_count=0;
SELECT COUNT(*) FROM table_request WHERE BILL_NUMBER=BN INTO #bn_count;
insert into table_test VALUES(BN,DN,#bn_count);
IF(#bn_count=0) THEN
INSERT INTO table_request VALUES (BN,DN,0,0,0);
END IF;
BLOKB:BEGIN
DECLARE CAT VARCHAR(5);
DECLARE T1 FLOAT;
DECLARE data_fetch CURSOR FOR SELECT CAT_ID, TYPE_1 FROM transactions WHERE ID=BN;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows_sub = TRUE;
OPEN data_fetch;
sub_loop: LOOP
FETCH data_fetch INTO CAT,T1;
IF no_more_rows_sub THEN
CLOSE data_fetch;
LEAVE sub_loop;
END IF;
IF (CAT='P1') THEN
UPDATE table_request SET A_TOTAL = A_TOTAL+T1 WHERE BN_NUM=BN;
ELSEIF (CAT='P2') THEN
UPDATE table_request SET B_TOTAL = B_TOTAL+T1 WHERE BN_NUM=BN;
ELSEIF (CAT='P3') THEN
UPDATE table_request SET N_TOTAL = N_TOTAL+T1 WHERE BN_NUM=BN;
END IF;
END LOOP sub_loop;
END BLOKB;
SET #SERVED_COUNT = #SERVED_COUNT+1;
END LOOP the_loop;
insert into table_test values ('End the_loop',0,NULL);
END BLOKA;
END WHILE;
END
I'm testing the code with a subset from the original table. And the subset has 461 records.
And with this code there should be 5 iterations. And in the final iteration the view should contain only 61 records. But with this code each iteration has 100 records, and during each iteration it takes into consideration only the first 100 records.
But if i run SELECT * FROM temp_view in mysql console after the procedure has run, it shows the last 61 records.
Anybody can point out what i'm doing wrong here?
I'm working on an old database already in use for years and really crappy designed.
There is a table, "Articles", which contains a "code" column that will be our PK.
And many tables like "idXXXXX" where XXXXX is a "code" value with exactly the same structure.
I looked at the application using this database and saw that relations between tables is made there.
I'm not affraid of redesign the database access in the application, but I don't want to lose years of entries in the database.
I want to create a "campain" table which will have an "id" PK and a "id_code" as FK linking "campain" to "articles"
I'm not a SQL master but I know I can get tables names with
SELECT TABLE_NAME FROM INFORMATION_SCHEMA WHERE TABLE_NAME LIKE 'id%'
But I have really no idea about how to deal with the result (which is fine).
So how can I access to every tables named "idXXX" and insert every rows in the "campain" table + set "id_code" column to "XXX"?
Here is the procedure I saved (I didn't add every fields in the INSERT line for testing purpose) :
CREATE PROCEDURE JoinAllTables()
BEGIN
DECLARE done INT default 0;
DECLARE tableName CHAR(9);
DECLARE buffStr CHAR(7);
DECLARE buffId INT default 0;
DECLARE cur1 CURSOR FOR SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE 'id%';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO tableName;
IF done THEN
LEAVE read_loop;
END IF;
SET buffStr = SUBSTRING(tableName, 3);
SET buffId = CAST(buffStr AS SIGNED);
set #sql = CONCAT("INSERT INTO campagnes(id, id_code) SELECT null, bufId FROM ",tableName); # Dynamically building sql statement
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur1;
END;
As u can see, I sub 'idXXXXX' to 'XXXXX' then CAST it AS INTEGER (SIGNED).
But I guess that in the "INSERT INTO" line, second tableName doesn't point to the variable. That's why I'm getting a
"#1446 - Tabble 'bddsoufflage.tablename'doesn't exist" Error :) Any idea ?
Edit: Updated answer
We can't have the tableName dynamically changed inside a prepared statement, so we must go through DynamicSQL to build the query using CONCAT, then compile the SQL with PREPARE, EXECUTE it and DEALLOCATE it.
DELIMITER //
CREATE PROCEDURE JoinAllTables()
BEGIN
DECLARE done INT default 0;
DECLARE tableName CHAR(9);
DECLARE buffStr CHAR(7);
DECLARE buffId INT default 0;
DECLARE cur1 CURSOR FOR SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE 'id%';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO tableName;
IF done THEN
LEAVE read_loop;
END IF;
SET buffStr = SUBSTRING(tableName, 3);
SET buffId = CAST(buffStr AS SIGNED);
set #sql = CONCAT("INSERT INTO campagnes(id, id_code) SELECT null, ", buffId, " FROM ",tableName); # Dynamically building sql statement
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur1;
END; //
See also this answer MySQL Pass table name to cursor select
Old answer
The procedure should look something like this. Thanks Mchl for providing an Insert Into query example, I simply added it to the rest of the procedure.
DELIMITER //
CREATE PROCEDURE JoinAllTables()
BEGIN
DECLARE done INT default 0;
DECLARE tableName CHAR(7); # Variable to contain table names CHAr(7) is assuming id + 5Xs as characters.
DECLARE cur1 CURSOR FOR SELECT TABLE_NAME FROM INFORMATION_SCHEMA WHERE TABLE_NAME LIKE 'id%'; # Create a cursor to iterate over the tables
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO tableName;
IF done THEN
LEAVE read_loop;
END IF;
#Your Insert statement here, using tableName as a field.
INSERT INTO campain (id, id_code, otherfields) SELECT null, tableName, otherfields FROM tableName;
END LOOP;
CLOSE cur1;
END;//
Easiest way would be to run the information_schema query you have within some script (PHP,Python,Perl - whichever suits you best) and use it's results to create queries like:
INSERT INTO
campain (id, id_code, otherfields)
SELECT
null, 'idXXXX', otherfields FROM idXXXX